6

Is it possible to use the so-called bang-commands (! + command/symbol, like e.g. !! or !$:p) in bash scripts and in bash aliases?

I wanted to add the line

alias getit='sudo apt-get install !!'

to my ~/.bashrc file to be able to install a not found command by typing getit after the non-successful call to the command itself, similar to the behavior of the tool The Fuck, just with less swearing...

However, this line does not work as can be seen in the output below:

$ cowsay
The application »cowsay« is currently not installed.
You can install it by typing:
sudo apt-get install cowsay

$ getit
Reading package lists... Done
Building dependency tree       
Reading state information... Done
E: Unable to locate package !!

$ 

Note that I translated the output of the first command myself from German, as LANG=C did not work on that... For whatever reason.

Anyway, you can see that the bang-command in the alias does not get interpreted to be replaced with the last executed command but stays the same string. I already found out that bang-commands usually only work within the interactive shell and therefore not within scripts.

Can this setting be changed, so that !! gets interpreted as the last executed command string within scripts? How about aliases, is it possible there?

  • It's not possible in aliases, I tried myself before with no success – Maythux May 29 '15 at 15:01
  • @Maythux What about scripts? Or might there eventually be a workaround to achieve the same functionality as !! without bang-commands but regular commands? – Byte Commander May 29 '15 at 15:03
  • I really have same question of you in mind, I don't really know what is the matter for that I just answered you for what I've faced before – Maythux May 29 '15 at 15:04
  • 3
    Just a couple of comments on your use-case: (1) you can't rely on the name of the package being the same as the command and (2) at a minimum you'd probably want !:0 rather than !! in case your original command included arguments. – steeldriver May 29 '15 at 15:30
  • Indeed, I think there was a recent question about getting the command not going program doing the installation as well. – muru May 29 '15 at 17:05
10

History expansion is done before alias expansion, so you'll have to use the history built-in to perform the expansion yourself:

alias getit='sudo apt-get install $(history -p !!)'

However, the above alias uses the whole last executed command as argument for sudo apt-get install, which means if we had given an argument to the failed command (like cowsay "Hello"), we would now also have this argument appearing in the list of packages to install (sudo apt-get install cowsay "Hello").

As this is not intended, we should modify the alias as steeldriver suggested and use !:0 instead of !! to only get the first word (the command name):

alias getit='sudo apt-get install $(history -p !:0)'

We still have to take care that this procedure only works, if the failed command name and its package name are equal. It would e.g. not work for the command lynx (a CLI browser), because it is included in the package lynx-cur.

  • Thank you, this works. Would you please add steeldriver's comment to your answer and provide the alternative with $(history -p !:0) to only return the first word of the command, so that arguments are left out? – Byte Commander May 29 '15 at 17:53
  • @ByteCommander don't wait for me, edit it in! – muru May 29 '15 at 18:14
  • Okay, I added what I thought is useful. – Byte Commander May 29 '15 at 18:25

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.