144

I used

history | less

to get the lines of previous commands and from the numbers on the left hand side I found the line I wanted repeated (eg. 22) and did

!22

at the command prompt and it worked -- executing the set of commands on the line I did at that time. I cannot figure out where the exclamation mark is used, what does it represent in terms of actions taken by bash, and where to use it. From the documentation I do not see an explanation that is 'tangible'.

2
  • 7
    This answer might help Nov 3, 2010 at 21:50
  • 10
    Not an answer to your question, but <ctrl>+R will allow you to interactively search your history and then immediately execute if you find what you were looking for.
    – kasterma
    Nov 3, 2010 at 22:25

5 Answers 5

195

! invokes history expansion, a feature that originally appeared in the C shell, back in the days before you could count on terminals to have arrow keys. It's especially useful if you add the current command number to the prompt (PS1="\!$ ") so you can quickly look at your screen to get numbers for past commands.

Now that you can use arrow keys and things like Ctrl-R to search the command history, I don't see much use for the feature.

One variant of it you might still find useful is !!, which re-executes the previous command. On its own, I don't find !!Enter any faster than just Enter, but it can be helpful when combined into a larger command.

Example: A common pilot error on sudo based systems is to forget the sudo prefix on a command that requires extra privileges. A novice retypes the whole command. The diligent student edits the command from the shell's command history. The enlightened one types sudo !!.

Processing ! in this way is enabled in Bash by default in interactive shells and can be disabled with set +o histexpand or set +H. You can disable it in Zsh with set -K.

6
  • 7
    I find Ctrl-P Ctrl-J to be pretty fast; faster than Up Enter, at least.
    – ephemient
    Nov 5, 2010 at 1:55
  • 32
    If you just want to run the last command, Up/Enter is fine, but if you want to add to it in some way (say, you forgot to sudo, or something), then you can do sudo !!, for example. That might be a bit faster than "Up/Ctrl-A(or Home, if you are lucky enough)/sudo/space/enter". YMMV. :)
    – malvim
    Jan 14, 2011 at 20:57
  • 1
    @AquariusPower: Just quote it. Either echo 'Hi!' or echo "Hi!" or echo Hi\!. All do the same thing. Jul 18, 2013 at 16:14
  • 3
    kudos on the enlightened ones :) can't believe I've only been a diligent student this whole time...
    – Pedreiro
    Oct 23, 2019 at 6:07
  • 2
    Important to mention that if you leave a space between the ! and the command you will be negating its exit code(Boolean not operator).
    – Roland
    Apr 10, 2020 at 12:01
65

If there isn't a longer answer here there's certainly one on Super User, since I've read one recently. In the bash man page you can find a huge section titled HISTORY EXPANSION on the matter.

You can do a whole host more than just run the last command, or command number X. You can do things like !cat to run the last command that started with cat. Or !?bash?:s/bash/csh/ runs the last command containing bash but replaces it with csh.

4
  • 5
    Although not directly an answer to OP's question, this needs way more upvotes. So useful! Thanks, Havok.
    – malvim
    Apr 30, 2015 at 19:09
  • Uh, I should really look into the man page before I start googling.
    – erikbstack
    Jul 27, 2015 at 13:39
  • 2
    !!:s/bash/csh/ actually runs the last command, then replaces bash with csh, if present. Not the last command that included bash. That would be !?bash?:s/bash/csh/ or maybe !?bash?:s/%/csh/
    – cde
    Jan 24, 2016 at 14:31
  • Having used Bash extensively for several years, I was shocked to find that I had never known about this keyword before, and even though I have spent hours poring through the man pages, this feature is incredibly obscure. Thank you for pointing out the relevant section--that's the only place in the actual docs where they explain it sufficiently. Jan 30, 2019 at 9:19
53

A lot more can be done with ! such as:

  • execute a command which is typed before 3 commands: !-3
  • execute a command that starts with !ls

and a lot more. See 15 Linux Bash History Expansion Examples You Should Know

3
  • Upvoted this. None of the other answers had any of these clever tricks. Feb 15, 2017 at 14:38
  • Upvoted because www.thegeekstuff.com is simply a fantastic resource, and this was one of the few linux articles I hadn't seen before. Very helpful. Jan 30, 2019 at 9:16
  • This is what I was looking for. Jan 24, 2020 at 20:39
15

Of course you can do !! to reuse the last command in bash shell. And then there is !$ to reuse the last part of your last command.

e.g. view some file

less path/to/your/file.txt

If you now want to edit the same file, you can use !$ to get only the file path from the last command

vim !$

EDIT:

You can use all the arguments of the previous command with !*

e.g.: This will create some files and remove all of them

$ touch a.txt b.txt c.txt d.txt
$ rm !*

Again , you can use a specific argument of the previous command

e.g. This creates 4 files and will remove the 3rd file (c.txt)

$ touch a.txt b.txt c.txt d.txt
$ rm !:3

Similarly, you can use a range of arguments from the last command as follows

$ touch a.txt b.txt c.txt d.txt
$ rm !:2-4

This will reuse arguments 2 to 4 which evaluates the expression to rm b.txt c.txt d.txt

4
  • 2
    Ah, I was hoping to find this. How does it behave with two args?
    – Jonathan
    Jun 6, 2019 at 16:23
  • 1
    It'll just reuse the last argument of the two. $touch f1 f2 and then $vim !$ will evaluate to $vim f2 Jun 7, 2019 at 4:33
  • 1
    There's also a way to reuse argument ranges. I don't remember the exact syntax, but it would go something like this: !:2- Jan 24, 2020 at 20:40
  • 1
    @SridharSarnobat Yeah apparently you can. Just a did a small research and it's super useful. I'll update the answer Mar 20, 2020 at 10:37
6

A friend of mine emailed me this:

It's part of GNU history library. In bash it is used to re-run commands in your history. If you want to be hardcore, grep for history_expansion_char in bash-4.1/lib/readline/histexpand.c for implementation details.

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.