The bash history command

Sometimes I run through the search terms people use within my site, or to get to my site, and I see some interesting stuff. Over the years, I’ve written perhaps hundreds of technical blog posts and articles at various sites and in magazines (and a book), but I have never once touched on the “history” command. In spite of that, someone searched for “what does the history command do”, and somehow landed on this site. Here’s a quick overview I give people who take my Linux From the Ground Up class (I’m currently booking on-site training for the July-September period, by the way).

The history command is built into the bash shell (and zsh, and other shells as well). This means you won’t find it sitting around in /usr/bin. You also will get funny results if you run “man history”, which will bring you to a man page about all of your bash builtins instead of one specific man page for “history”.

Typical (Simple) Usage

You have a shell session open. You’ve been doing some work, and you’ve run several commands. One of the commands was a long nasty compilation command which contained environment variable settings, lots of flags, and references to specific files. It was long, and you don’t want to type it again, but you have to because the compilation failed. You’ve fixed the issue, and want to run the same compilation command.

Instead of typing all of that stuff in all over again, you have a couple of choices (more really, but we’re starting slowly):

  1. If you’re on a machine that uses readline capabilities, you can use the up arrow to move back through your previous commands. This is inefficient.
  2. You can type “history” at the command line

Typing “history” at a shell prompt by itself will print a numbered list of commands you have run. Let’s say your compile command is number 50 in the list. Now typing “!50″ at the command prompt will run that command again without you having to type in the whole disgusting thing.

More Useful Usage of “history”

If you know that you’ve only run, say, “netstat” with one set of arguments, and you keep running it over and over again, you can use a shortcut by just typing “!netstat”. That will run that last command in the history that starts with “netstat”. This is prone to error if you run a command often, but with different arguments.

If you need to run a long ugly command again immediately, but you need to change just a single argument, you can also use carets to do string substitution. So, if you just ran “netstat -plant | grep :80″ and you now want to check for “:22″ instead of “:80″, instead of typing in the whole command again, you can just type “^80^22″. Note that if you ran, in this order:

  1. netstat -plant | grep 80
  2. netstat -plant | grep 111
  3. ^80^22

You’ll get an error. It only works on the last command you ran.

You can also search through your command history in bash using Ctrl-R. This will alter your prompt, and when you start typing the command you’re looking for, it will do an incremental search through your command history and put what it finds on the line. When the right one pops up, hit enter, and the command will run. Note that if you need to make a quick edit to the command before it runs, moving the cursor will paste the command on your prompt, which will return to normal, and you can edit it before hitting enter.

History Command Quirks

There’s one quirk of history that bites lots and lots of people, myself included. If you’re aware of it, you can avoid it biting you. You see, bash keeps its command history for the current shell in memory, and then writes out that memory to your ~/.bash_history file… when the shell exits. There are times when this can be problematic, for example if you open another shell and want to run a command you just ran in the first shell. It won’t be in your history, because the first shell is still open, so its history hasn’t been written out to disk yet.

There are two ways to get the history that’s in memory onto disk. The first is to exit the first shell. The second is to run “history -w” in the first shell. Either will write the history to the ~/.bash_history file, but be forewarned that doing this overwrites anything that was previously in the ~/.bash_history file! Maddening, isn’t it?

If you configure bash ahead of time by adding “shopt -s histappend”, you can tell bash to append, rather than overwrite the history file.

Another quirk of how history is setup to work by default is that it saves every single command you type. If you open a terminal and type nothing but:

ls
cd
cd -
ls
ls -l

Those all take up space in your history. The environment variable HISTSIZE can be set to something like 10,000 to account for this if you want. It’s set to 1000 by default on Red Hat systems. The other thing you can do is tell the history mechanism to conserve space in history by ignoring certain commands, patterns, duplicates, etc, using HISTCONTROL and HISTIGNORE

HISTCONTROL can be set to “ignorespace”, which will not put commands starting with a space in history. I’ve never been able to train myself to type a space before ‘ls’, so I’ve never used that setting. Slightly more useful is the “ignoredups” setting, which will stop the command you just ran from getting into history if it’s a duplicate of the command run just before it. Still not what I’m looking for. Better is “erasedups”, which will delete all previous instances of that command in history before writing the current command to history.HISTCONTROL can take multiple values, separated by colons.

If you just want to make sure “ls” commands never get written to history, you can just use HISTIGNORE for that. It can be set to a colon-delimited list of patterns or commands that, if matched, disqualify the command from being entered into history. So, you can do something like this:

HISTIGNORE=ls:cd:df:du

Note that these are essentially command matches, and must match exactly, starting from the beginning of the line. So, ” du” (with a space in front) will be put into the history list, and so will “du -h” (no preceding space) will also make it. You can use, for example, HISTIGNORE=du* if you want to catch that command and anything that follows it.

Really Hardcore History

There’s a presentation online that talks about way more nitty-gritty details of history than I have time to cover. It’s great work, though, so I recommend you check it out here.




								
				

			
  • Aaron

    Thanks for this post — couldn’t figure out why lines starting with a space weren’t in my history (that option was getting pulled in from a corporate bashrc file). That is a horrible option.

  • Ivo Bratanov

    Thanks for the post! I’ve been curious about the specifics of the linux history for a while.

  • Colin

    Thanks for the post. I was interested in learning more however the link you provide for the more in-depth history goes to a 404 page. Did you link wrong by mistake or did they take it down? Thanks.