Ubuntu – Use the bang-commands (e.g. ‘!!’) of bash in scripts and aliases

bashbash-historybashrccommand linescripts

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?

Best Answer

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.