Access to last command in Zsh (not previous command line)
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:
!# Refer to the current command line typed in so far.
So you can do the following to get previous command:
echo "This is command #1"; var=$(echo !#) && echo "The previous command was:" \'$var\'
And the result is
This is command #1
The previous command was: 'echo This is command #1'
You can also play with
echo "This is command 1"; var=$(echo "!#:s/;//") && echo "The previous command was:" \'$var\'
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:
ls >/dev/null 2>&1; var=$(echo "!#:s/;//") && print "The previous command was:" \'$var\'
gives the output:
ls >/dev/null 2>&1; var=$(echo "ls >/dev/null 2>&1") && print "The previous command was:" $var
The previous command was: 'ls >/dev/null 2>&1'
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, thus var
in the expression
echo "This is command #1"; var=$(echo !#) && echo "The previous command was:" \'$var\'
is in fact equal to:
var=$(echo echo "This is command #1";)
so that echo $var
prints echo This is command #1
as mentioned before.
But if we write simplified version, without variable:
echo "This is command #1"; echo "The previous command was:" !#
then !#
would contain additional, second echo
and its argument, so actually command would become:
echo "This is command #1"; echo "The previous command was:" echo "This is command #1"; echo "The previous command was:"
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.
An attempt to answer my own question. I still welcome other's answers, as this one is not perfect.
At least, there's a way to get the same as with Bash, using the $history
array and the $HISTCMD
index. Example:
$ zsh
$ echo "This is command #3"; echo $history[$HISTCMD]
> This is command #3
> echo "This is command #3"; echo $history[$HISTCMD]
This gives back the same as history | tail -n1
in Bash and I can apply sed
on this. But relying on sed
is precisely the reason why this answer is not totally perfect; it's error‑prone to parse a command line with regular expressions, especially with an as much complex shell syntax as that of Zsh is.