Simon Tatham wrote an article recently called “Policy of transience”, explaining (among other things) why you might want to disable your shell history file. Simon writes:
My unusual habit is: turn off the history file completely, by putting the command ‘
unset HISTFILE
’ in my.bashrc
. I still get history within a single instance of the shell, so I can edit my last command ten times until it works properly; but history isn’t shared between my terminal windows, or preserved when I log out and log in again. All the shell history I allow myself is localised and short-term.[…]
If I type a shell command that’s valuable – one that did something useful enough that I might want it again in future, and long and complicated enough that I’d be annoyed to have to figure it out a second time from scratch – then I can’t rely on it just happening to be in my
.bash_history
. So instead I put it somewhere else: maybe a shell function in my.bashrc
, or maybe a shell script in my directory of random useful scriptlets. Or maybe just in a file of notes jotted down to myself about useful shell runes to remember.I find this a more useful way to remember shell commands. Firstly, this procedure separates the working version of the command from all the failed attempts just before it. Even within the context of one instance of
bash
I’ll sometimes accidentally recall a wrong version of a command when I was aiming for the corrected one two commands later; the idea of having a year’s worth of my own false starts available for accidental recall seems horrifying! Instead, I deliberately save just the working version, and let all the failed attempts go in the trash when I close the shell.
For me, this idea feels uncomfortable! If anything, I’m a shell history maximalist; I have zsh configured to save my last 9,800 commands. I rely heavily on my shell history to remember how I did things before. I suspect my most-used form of zsh completion is to complete the current command line from a history entry.
But I also think Simon makes a good point about how useless it is to record all of your false starts. Why save cd ~/Dekstop
, which I’m never going to want to run again? Why save vim /etc/rc.conf
when sudo vim /etc/rc.conf
is what I meant 100% of the time? At best, these dead-end commands are taking up unnecessary space in my history file. At worst, they’ll trip me up again the next time I need to do the same thing and naively run the first command from my history that looks right.
For myself, I don’t want my shell history to be opt-in. But I can make it easier to remove the typos and dead ends. I came up with this zsh function to facilitate that:
function smite() {
setopt LOCAL_OPTIONS ERR_RETURN PIPE_FAIL
local opts=( -I )
if [[ $1 == '-a' ]]; then
opts=()
elif [[ -n $1 ]]; then
print >&2 'usage: smite [-a]'
return 1
fi
fc -l -n $opts 1 | \
fzf --no-sort --tac --multi | \
while IFS='' read -r command_to_delete; do
printf 'Removing history entry "%s"\n' $command_to_delete
local HISTORY_IGNORE="${(b)command_to_delete}"
fc -W
fc -p $HISTFILE $HISTSIZE $SAVEHIST
done
}
Running smite
opens an fzf-powered browser with your shell history in it. (So you’ll need fzf installed, but that’s the only dependency.) By default, you’ll only see your history from the current session, but running smite -a
shows all of your history.
When you navigate to an entry and press Return or Enter, zsh will delete all instances of that command from your history. If you want to delete multiple commands at once, you can select them by pressing Tab on each one and then typing Return to commit your changes.
Multiline commands are not handled correctly, for now.
The function prints back all of the commands it’s deleting, just in case you select the wrong one.
Here’s a video:
The approach is taken from this Stack Overflow answer by Marlon Richert, who later wrote a zsh plugin called zsh-hist to make the history system easier to deal with. (I haven’t tried the plugin, but it seems to make it possible to delete the history entry with a given number, which is the omission in zsh that prevents multi-line commands from being easy to delete.)
Even if my zsh code isn’t relevant to you, I hope you’ve taken a moment to consider what you want out of your shell history and if there’s anything you could tweak to get closer to that.
Since adding this function to my zshrc, I’ve been paying more attention to which commands are misfires, and pruning the ones that are. If my history file is never quite going to be a beautiful garden, I can at least put more effort into removing the weeds.