Bash – Get only the second last argument of the previous command

bash

[ -f /root/.bash_aliases ] || echo "$_"

gives me ]

I want /root/.bash_aliases

I also tried

[ -f /root/.bash_aliases ] || echo !:2

but it gives me the following result

[ -f /root/.bash_aliases ] || echo /root/.bash_aliases
/root/.bash_aliases

I only want /root/.bash_aliases

Best Answer

That's the kind of thing functions are made for:

print_if_not_regular() {
  [ -f "$1" ] || printf '%s\n' "$1"
}
print_if_not_regular /root/.bash_aliases

Some shells like es or zsh have anonymous functions:

(){[ -f "$1" ] || printf '%s\n' $1} /root/.bash_aliases # zsh
@ {[ -f $1 ] || printf '%s\n' $1} /root/.bash_aliases # es

I'd avoid using $_. It no longer works if you have a DEBUG trap for instance.

$ bash -c 'trap ": \"\$((cmdcount+=1))\"" DEBUG; echo foo; echo "$_"'
foo
2

Note that !:n is not the nth argument of the last command, but the nth lexical token of the previous command line (the last line (possibly even multiline) stored into the history). For instance, in

echo $(echo A B)
echo foo; echo !:1

That !:1 is not expanded to foo (the last argument of the last command), nor B (the last argument of the echo from the echo command of the previous command line), but $(echo A B). Here you'd need to enter:

[ -f /root/.bash_aliases ] ||
  echo !:2

That is, enter it on separate lines. Here, it happens to work (by accident) even though the history line is not fully complete by the time you enter that second line. That same trick doesn't work in (t)csh (where history expansions comes from), nor zsh.

Related Question