Bryan Dyck bio photo

Bryan Dyck

Twitter Github

I was recently working on automating some server management tasks and didn’t want some of the shell commands to show up in the shell history, only I’d quite forgotten how to make that happen. As it turns out, it’s fairly straightforward but it led to an interesting rabbit trail into some less-used parts of the history command, which I’m documenting here for the next time I forget.

An ounce of prevention

If you’re going to run a command that is possibly unsafe or contains sensitive information (i.e. passwords), the best solution is to prevent it from showing up in your history in the first place. This can be accomplished by setting the $HISTCONTROL environment variable to ignorespace:ignoredups (ignoredups tells Bash to ignore duplicate commands and saves a bit of space in your history list) or ignoreboth.1 Once that’s set, you can precede a command with a space and it won’t show up in your history:

# Note extra space
$  foo-bar --with-password d34db33f

A pound of cure

If—as in my case—you’ve already run a command that you’d rather not have recorded in history, you can remove it. If you’re still using the same shell session as when you executed the command the history hasn’t hit the disk yet so you can delete the last-executed command as follows (note that this requires ignorespace to be set or else it will just delete itself from history!):

$  history -d $((HISTCMD-1))

If you wish to remove say, the last four commands, you can do the following:

$  history 4
$ 100  foo --opt 'very secret!'
$ 101  sensitive-thing
$ 102  oops
$ 103  not-safe

$  for i in {103..100}; do history -d $i; done

$ # Without ignorespace
$ for i in {104..100}; do history -d $i; done

Of course, you can always exercise the nuclear option and just kill the session, discarding shell history for that session entirely:

$ kill -9 $$

In the case where the commands you wish to remove are from an earlier shell session, they will have already been appended to the history file (pointed to by $HISTFILE, which defaults to ~/.bash_history) and performing the manipulations above and exiting will merely append the new commands to the shell history (on exit). To remedy that, you need to overwrite the history file with the shell history as it stands after your manipulations:

# Delete commands with history -d as above
$  history -w

You can also sidestep the history -d and history -w dance and directly edit $HISTFILE in your favourite text editor.

Addendum: Other useful history settings

If you’re not already a regular user of Bash’s history facility, there are some additional settings that can increase its usefulness which you can add to your ~/.bashrc file (these are taken from my own settings):

1 shopt -s histappend >/dev/null 2>&1
2 HISTIGNORE="ls:[bf]g:exit:pwd"
3 HISTTIMEFORMAT="%F %R%z "
4 HISTFILESIZE=5000
5 HISTSIZE=5000

The terseness of Unix… Here’s the line-by-line breakdown:

  1. Setting histappend tells Bash to only append history instead of overwriting it (usually the default but this way it’s clear). (The output redirection is just for tidiness)
  2. $HISTIGNORE specifies a colon-separated list of commands that you wish to exclude from your history; for instance, saving ls with no arguments isn’t particularly useful.
  3. $HISTTIMEFORMAT specifies a timestamp format to use when displaying history with the history command. Timestamps are handy for both documenting when something happened as well as being searchable. A sample line: 1234 2014-04-01 12:00-0700 python -v
  4. $HISTFILESIZE specifies the maximum number of lines that should be kept in $HISTFILE. If this limit is exceeded after appending new entries, then the file will be truncated to $HISTFILESIZE (to be clear: the oldest items are removed, not the newest).
  5. $HISTSIZE specifies the maximum number of commands to retain in memory during an interactive shell session. This also specifies the number of commands from the in-memory history list that will be copied to $HISTFILE when the shell exits. (Note: this value does not have to be equal to HISTFILESIZE)

Last but not least, if you need more detail (or want a different point of view) it’s a Google search away. Please add comments or corrections, too—misinforming is almost worse than not informing at all.

  1. This variable is not necessarily set by default: my Ubuntu server defaults to ignoreboth but neither of my Mac OS X boxen had it set.