Is there a way to retrieve the text of the last command in Zsh? I mean the last executed command, not the last command line.
What was tried
Trying to do it, I discovered a subtle difference between Zsh and Bash handling of history. This difference is exposed in the following sample, which is the base of what I was doing in Bash, and which does not work in Zsh.
$ zsh
$ echo "This is command #1"
> This is command #1
$ echo "This is command #2"; echo $(history | tail -n1)
> This is command #2
> 3807 echo "This is command #1"
$ bash
$ echo "This is command #1"
> This is command #1
$ echo "This is command #2"; echo $(history | tail -n1)
> This is command #2
> 4970 echo "This is command #2"; echo $(history | tail -n1)
Same tests, whose result differs in the last line. Bash appends the command line to the history, before execution (I don't know if it's by specification), while Zsh seems to append the command line to the history, after execution (the same, I don't know if it's by specification), thus history | tail -n1
does not give the same.
What I want, is to be able to retrieve echo "This is command #2"
, that is, the text of the previous command even when it's on the same command line as other commands (when there are multiple command separated with ;
).
With bash, I could use history | tail -n1 | sed ...
, but this does not work any more with Zsh, due the difference in history handling.
What needs to be achieved
Any idea on how to get the last command from a multi‑command command line in Zsh?
I need it for when a command needs to know what was the previous command, whatever that command was on the same line or the previous line. Another way to say it, could be: I need to access the last item of a single command oriented history, not of a command(s) line oriented history.
Best Answer
As far as I'm aware of there is no easy way to this in zsh. I believe the closest answer to your question in interactive shell is to use history expansion mechanism, especially
!#
, but you may be interested in!!
,!!:1
and others as well. From zsh manual:So you can do the following to get previous command:
And the result is
You can also play with
if you would need to enclose
!#
with double quotes, but in this case you have to remove;
from the end of the "last command".:s/;//
is doing that. More sophisticated commands are now working well:gives the output:
Notice however that in this case
#
should not be present in the first command together with quotes as it will be interpreted as comment.Note 1
It is important here that
!#
represents everything what is present in the current line so far except a current atom, thusvar
in the expressionis in fact equal to:
so that
echo $var
printsecho This is command #1
as mentioned before. But if we write simplified version, without variable:then
!#
would contain additional, secondecho
and its argument, so actually command would become:Note 2
This solution is obviously not perfect because, as you may noticed, quotes are not included into variable. You can try to workaround about it with appending single quotes around
!#
but not directly (otherwise history expansion won't work).Note 3
If you have more than 3 or more commands in one line, separated with
;
, than you would need to play a little bit more with zsh modifiers or with outside commands like sed or awk.