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.

Tags:

Zsh