The question is about special variables. Documentation says:


designates the last argument of the preceding command. This may be shortened to !$.

($_, an underscore.) At shell startup, set to the absolute pathname used to invoke the shell or shell script being executed as passed in the environment or argument list. Subsequently, expands to the last argument to the previous command after expansion. Also set to the full pathname used to invoke each command executed and placed in the environment exported to that command.

There must be some difference I cannot catch, because:

$ echo "hello" > /tmp/a.txt
$ echo "!$"
echo "/tmp/a.txt"

$ echo "hello" > /tmp/a.txt
$ echo $_

What is the difference?

  • 5
    !$ is history expansion, not a special variable, so I'd assume that the bash code handles them differently. – Jeff Schaller Mar 23 '16 at 11:03
  • 3
    I don't have a definitive answer to this question, but: ① The Bourne shell language is one of, perhaps the, least internally consistent programming language still widely used today. Attempting to deduce general principles will get you nowhere. You just have to memorize all the special rules. Sorry. ② Note the words "after expansion", immediately after the phrase you highlighted, in the definition of $_. That's crucial. It might not explain the difference that puzzles you but it does explain a bunch of other ways $_ and !$ are not the same. – zwol Mar 23 '16 at 13:00

!$ is a word designator of history expansion, it expands to the last word of previous command in history. IOW, the last word of previous entry in history. This word is usually the last argument to command, but not in case of redirection. In:

echo "hello" > /tmp/a.txt

the whole command 'echo "hello" > /tmp/a.txt' appeared in history, and /tmp/a.txt is the last word of that command.

_ is a shell parameter, it expands to last argument of previous command. Here, the redirection is not a part of arguments passed to the command, so only hello is the argument passed to echo. That's why $_ expanded to hello.

_ is not one of shell standard special parameters anymore. It works in bash, zsh, mksh and dash only when interactive, ksh93 only when two command are on separated lines:

$ echo 1 && echo $_

$ echo 1
$ echo $_
| improve this answer | |
  • 7
    One effect of !$ expansion vs $_ parameter is that echo $_ always shows in history as echo $_, while echo !$ shows the value that it was expanded to. E.g. echo one two then echo !$, then pressing up shows echo two. – Jacktose Oct 9 '19 at 17:24
  • Not sure I understand what "only when interactive" applies to. I was just able to do something along the lines of mkdir -p foo && for f in ${array_of_filenames[@]}; do mv $f $_; done in a bash script – Fons MA Aug 18 at 4:57

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.