The history event designator !!
is replaced by the last command in your history. Bash first prints out the command how it will be executed, then executes it.
Example:
$ foo
foo: command not found
$ !!
foo # command to be executed
foo: command not found # result of execution
In your case:
$ echo !
!
$ echo !!
echo echo ! # command to be executed
echo ! # result of execution
$ echo !!!
echo echo echo !! # command to be executed
echo echo !! # result of execution
Note that a command with an event designator is not inserted into history as typed. First the event designator gets expanded and then the command is entered into history. That is why in the third command (echo !!!
), the event designator is not replaced by echo !!
(the typed second command), but by echo echo !
(the expanded second command).
Here is the last command again with the replaced part highlighted:
$ echo (!!)!
echo (echo echo !)! # command to be executed
echo echo !! # result of execution
Text between backticks is executed and replaced by the output of the command (minus the trailing newline characters, and beware that shell behaviors vary when there are NUL characters in the output). That is called command substitution because it is substituted with the output of the command. So if you want to print 5, you can't use backticks, you can use quotation marks, like echo "$b"
or just drop any quotation and use echo $b
.
As you can see, since $b
contains 5, when using backticks bash
is trying to run command 5
and since there is no such command, it fails with error message.
To understand how backticks works, try running this:
$ A=`cat /etc/passwd | head -n1`
$ echo "$A"
cat /etc/passwd |head -n1
should print first line of /etc/passwd
file. But since we use backticks, it doesn't print this on console. Instead it is stored in A
variable. You can echo $A
to this. Note that more efficient way of printing first line is using command head -n1 /etc/passwd
but I wanted to point out that expression inside of backticks does not have to be simple.
So if first line of /etc/passwd is root:x:0:0:root:/root:/bin/bash
, first command will be dynamically substituted by bash to A="root:x:0:0:root:/root:/bin/bash"
.
Note that this syntax is of the Bourne shell. Quoting and escaping becomes quickly a nightmare with it especially when you start nesting them. Ksh introduced the $(...)
alternative which is now standardized (POSIX) and supported by all shells (even the Bourne shell from Unix v9). So you should use $(...)
instead nowadays unless you need to be portable to very old Bourne shells.
Also note that the output of `...`
and $(...)
are subject to word splitting and filename generation just like variable expansion (in zsh, word splitting only), so would generally need to be quoted in list contexts.
Best Answer
$(...)
is a command substitution (not just a subshell), but$((...))
is an arithmetic expansion.When you use
$((...))
, the...
will be interpreted as an arithmetic expression. This means, amongst other things, that a hexadecimal string will be interpreted as a number and converted to decimal. The whole expression will then be replaced by the numeric value that the expression evaluates to.Like parameter expansion and command substitution,
$((...))
should be quoted as to not be affected by the shell's word splitting and filename globbing.As a side note, variables occurring in an arithmetic expression do not need their
$
: