I have a script named cpodin that copies several build artifacts to another directory. Afterward, I edit SHA1SUM in the destination directory.

For example:

cpodin /foo/bar/baz
vim !$/SHA1SUM

How do I make the latter command an alias named fixsha that takes no argument and edits SHA1SUM in the directory named by the previous command’s last argument? An alias of

alias fixsha='vim !$/SHA1SUM'

results in attempts to edit !$/SHA1SUM instead of /foo/bar/baz/SHA1SUM.


Bash does not perform history expansion after alias expansion, so you have to do so explicitly using history, documented in Bash History Builtins and excerpted below.

history -ps args

  • -p
    Perform history substitution on the args and display the result on the standard output, without storing the results in the history list.
  • -s
    The args are added to the end of the history list as a single entry.

An alias of alias fixsha='vim $(history -p !$)/SHA1SUM' will work most of the time but not as intended when !$ expands to a path that contains whitespace. Adding double-quotes around the path argument will protect any escapes on whitespace and results in vim "/foo/bar\ baz/SHA1SUM" rather than the desired vim /foo/bar\ baz/SHA1SUM or its equivalent vim "/foo/bar baz/SHA1SUM".

So in addition to explicit history expansion, eval is also necessary to unwrap one layer of quoting.

alias fixsha='eval vim "$(history -p !$)/SHA1SUM"'

Note: You may be surprised to learn that the double-quotes in the above alias definition are unnecessary. This is because no whitespace is between the expanded value of !$ and /SHA1SUM to trigger bash’s word splitting. However, I like them being there to emphasize the intent that it is expanding to a single argument.

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.