16

I'm using Ubuntu 21.10. If I mess up, I can re-run the last command with !!. Here's an example:

$ apt update
Reading package lists... Done
E: Could not open lock file /var/lib/apt/lists/lock - open (13: Permission denied)
E: Unable to lock directory /var/lib/apt/lists/
W: Problem unlinking the file /var/cache/apt/pkgcache.bin - RemoveCaches (13: Permission denied)
W: Problem unlinking the file /var/cache/apt/srcpkgcache.bin - RemoveCaches (13: Permission denied)
$ sudo !!
sudo apt update
[sudo] password for [me]:
...
Fetched 336 kB in 2s (206 kB/s)
Reading package lists... Done
Building dependency tree
Reading state information... Done
98 packages can be upgraded. Run 'apt list --upgradable' to see them.

That's fine, but instead of rerunning the previous command, how can I run the one before it? Like this:

$ echo hi
hi
$ echo hello
hello
$ !!   <-- I'm trying to get that to run 'echo hi'
hello

Basically, here's what I'm looking for

$ echo hi
hi
$ echo hello
hello
$ ???   <-- What can I put here to get it to run 'echo hi'?
echo hello!
hello!

So, given that !! runs the last command, how can I run the command before the previous one? In my example, instead of typing !!, what can I run to execute echo hi?

6
  • 6
    The shortcuts like !! were useful in plain old sh, where there was no command history and editing. In bash, there's no more need to use them as you can freely go to previous/next commands by pressing up/down arrow.
    – raj
    Jan 3 at 23:45
  • 3
    echo $(!-2), there's also a possibility to embed event designators.
    – user986805
    Jan 4 at 6:29
  • If you want to run the command before you ran the previous one, I'd suggest using a time machine add-on to bash. Whereas, if you want to re-run the command before last, that's a different story. Jan 4 at 10:52
  • Yes... "re-run the command before last" or "re-run Nth command from history" Jan 4 at 12:50
  • 1
    @raj In agreement with bac0n, I also use them for embedding. Sometimes I run one command, then another, then I want to compare the outputs, and do something like diff -u <(!-2) <(!!) or comm -23 <(!-2 | sort) <(!! | sort). Another common use of history expansion for me is !$ to reuse the last argument of the previous command in a new one, like aur fetch foo to make a build directory for an AUR package foo, then cd !$ to change the working directory to it.
    – JoL
    Jan 4 at 19:25

4 Answers 4

41

You can use the up-arrow and down-arrow keys to cycle through your command history. So to re-run the second-last command, you'd press up-arrow twice, then return.

This has several advantages over methods like !-2. Most importantly (in my opinion), you can see the command and make sure it's the one you intended to re-run before pressing return to execute it. Even aside from simple mistakes ("oops, I guess it was the third command back that I wanted"), bash history may not count previous commands the way you expect because of options like ignorespace, ignoredups, and the HISTCONTROL variable.

Also, interactive command recall lets you easily edit the command before re-running it. You can do that with history recall modifiers as well, but again doing it interactively lets you see your edits before executing the recalled/modified command.

Plus if you're lazy like me, up-arrow up-arrow return is easier to type than (shift)exclamation minus 2 return

4
  • 3
    "Most importantly (in my opinion), you can see the command and make sure it's the one you intended to re-run before pressing return to execute it." If you enable histverify then any command with history substitution will need to be confirmed before parsed by the shell. However, I am lazy like you so I would favor your suggestion in this particular case.
    – Quasímodo
    Jan 4 at 11:48
  • If you prefer to retain your hands around the home row, rather than wandering over the keyboard, an easier combo would be Ctrl-P (memorize as "Previous") instead of the UpArrow, and Ctrl-N (as for "Next") instead of the DownArrow. Thus, Ctrl-PP, Return.
    – Ruslan
    Jan 5 at 23:08
  • 1
    With a major downside being a lack of scriptability. At least in any sensible form.
    – Nate T
    Jan 6 at 12:56
  • 1
    You can also press M-^ (i.e. Alt+Shift+6 or ESC Shift+6) to initiate history expansion on the current line. Jan 11 at 4:19
36

From the HISTORY EXPANSION section of man bash:

!-n    Refer to the current command minus n

So to run the command before last, you can use !-2

In fact, !! is just a synonym of !-1

5
  • 4
    You mean computer scientists start counting from zero when going forward, but from -1 when going backwards? Where did these nerds learn to count? Jan 4 at 19:45
  • 1
    @MikeMaxwell must just be a quirk of history :-D
    – SusanW
    Jan 4 at 20:04
  • 9
    @MikeMaxwell, in this case, 0 is what you're currently entering; -1 is the previous command, -2 is the previous but one. It makes sense to me :-)
    – OmarL
    Jan 5 at 8:35
  • 1
    @OmarL So, if the next command I'm going to type is going to be echo "The stock market is going to be X" in two hours, does that mean I can use !1 to see what the stock market is going to be two hours from now? Jan 6 at 1:40
  • 1
    @HiddenWindshield That's ... creative, but no dice: -bash: !1: event not found
    – OmarL
    Jan 6 at 9:11
7

!-n execute command "-n" of your history, but I wouldn't recommend it, as it is "instant" and will not give you time to check first that the "Nth" command is the right one you need to execute again... ( @bac0n nicely remarked that you can set : shopt -s histverify to switch it to "non instant": it will let you verify the command !-n expands to, before executing it with <Enter>)

However, if you are using bash as your shell, I much prefer to recommend using the search feature:

Ctrl and then type something, which is the part of the command that you are looking for. (ex: Ctrl + r + (type into terminal) echo)

It will present the latest occurrence in your history matching something (i.e., matching the search term that you typed after Ctrl + r).

Subsequent Ctrl + r will look further back in your history. Or adding letters will precise the search.

And once you find a good match, press Enter to execute it again (or Ctrl + c to exit from the search without executing the currently displayed matching command).

Additional tip: if, instead of Enter , you press Ctrl + o , it will execute the selected history command AND present the next one, ready to be executed with Enter (which only execute that one and returns to the normal prompt) or Ctrl + o again (which executes it and present the next command in your history). Very useful to repeat a sequence of steps, and providing a display of those commands before executing them.

And each time you use those things, you are allowed to change something on the currently displayed line (using the left or right arrow key to move within it), such as a parameter, a server name, etc., and execute that modified line + go to the next one (if you pressed Ctrl + o).

7
  • 3
    It's depended on how you set up the shell option... you can append shopt -s histverify if you don't want to run the entry directly.
    – user986805
    Jan 4 at 11:16
  • 1
    Also, you might add that this is bash specific. Most modern shells have history tracking / rerun in one form or another, but the implementation and syntax / shortcuts will be different from one shell to the next.
    – Nate T
    Jan 4 at 21:23
  • thanks @NateT and bac0n , 2 good points I omitted in my answer. I edited it in. Jan 5 at 17:51
  • 1
    To complement Ctrl-R for reverse-i-search, there's Ctrl-S for forward i-search (useful when you missed the right entry and need to go "back"), though there's a caveat.
    – Ruslan
    Jan 5 at 23:14
  • @Ruslan adds an interresting thing, but I didn't bother to add that one as it is less often used and it is sometimes linked to other things (ctrl-s [stops the terminal from outputting characters] ctrl-q [resumes the character output], for exemple, by default in lots of shells. See unix.stackexchange.com/a/72092/27616 ). Some additionnal setting (of readline? and stty) may be required to set it all working, and exemples can be found on unix.se Jan 11 at 8:50
4

If you run

history

you'll get a list of the last 'n' commands entered. The history size defaults to 500 commands. So, as explained by steeldriver, the command before last one can be rerun with

!-2

and yes, the third to last will be

!-3

and so on. For more details, use

man history

and search for Event Designators.

2
  • 3
    A small addition: you can run a specific command from the history by just using !312 (to run the command with the line number 312 shown in your history output). Even more useful tip: If you had a typo in that command, you can run !312:p which will not execute the command but still put it on top as "last used command". This helps you in editing longer commands without manual copy-paste from the history by simply using arrow up and editing the command before running it.
    – kopaka
    Jan 4 at 10:34
  • Great! The list of modifiers of "history" is full of surprises. Thanks!
    – Latkar
    Jan 8 at 20:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.