访问Zsh中的最后一个命令(不是上一个命令行)

访问Zsh中的最后一个命令(不是上一个命令行),zsh,Zsh,有没有办法检索Zsh中最后一个命令的文本?我指的是最后执行的命令,而不是最后的命令行 尝试了什么 试图做到这一点,我发现Zsh和Bash对历史的处理有着微妙的区别。这个差异在下面的示例中暴露出来,这是我在Bash中所做工作的基础,在Zsh中不起作用 $ zsh $ echo "This is command #1" > This is command #1 $ echo "This is command #2"; echo $(history | tail -n1) > This i

有没有办法检索Zsh中最后一个命令的文本?我指的是最后执行的命令,而不是最后的命令行

尝试了什么 试图做到这一点,我发现Zsh和Bash对历史的处理有着微妙的区别。这个差异在下面的示例中暴露出来,这是我在Bash中所做工作的基础,在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)
相同的测试,其结果在最后一行中不同。Bash在执行之前将命令行附加到历史记录中(我不知道它是否按规范),而Zsh似乎在执行之后将命令行附加到历史记录中(同样,我不知道它是否按规范),因此
history | tail-n1
没有给出相同的结果

我想要的是,能够检索
echo“This is command#2”
,即上一个命令的文本,即使它与其他命令位于同一命令行上(当有多个命令用
分隔时)

对于bash,我可以使用
history | tail-n1 | sed…
,但由于历史处理的不同,这在Zsh中不再有效

需要实现什么 您知道如何从Zsh中的多命令行获取最后一条命令吗


当一个命令需要知道前一个命令是什么,无论该命令在同一行还是前一行,我都需要它。另一种说法是:我需要访问单个面向命令的历史记录的最后一项,而不是面向命令行的历史记录。

尝试回答我自己的问题。我仍然欢迎其他人的答案,因为这个答案并不完美

至少,有一种方法可以使用
$history
数组和
$HISTCMD
索引获得与Bash相同的结果。例如:

$ zsh
$ echo "This is command #3"; echo $history[$HISTCMD]
> This is command #3
> echo "This is command #3"; echo $history[$HISTCMD]

这将返回与Bash中的
history | tail-n1
相同的结果,我可以对其应用
sed
。但依赖sed恰恰是这个答案不完全完美的原因;使用正则表达式解析命令行很容易出错,特别是使用与Zsh一样复杂的shell语法。

据我所知,在Zsh中没有简单的方法可以做到这一点。我相信在交互式shell中,最接近您问题的答案是使用历史扩展机制,尤其是
#,但您可能对
感兴趣
!!:1
以及其他。从zsh手册:

!#请参阅so中键入的当前命令行 远

因此,您可以执行以下操作以获取上一个命令:

echo "This is command #1"; var=$(echo !#) && echo "The previous command was:" \'$var\'
结果是

This is command #1
The previous command was: 'echo This is command #1'
你也可以玩

echo "This is command 1"; var=$(echo "!#:s/;//") && echo "The previous command was:" \'$var\'
如果您需要附上
#
带有双引号,但在这种情况下,您必须删除
从“最后一个命令”的末尾开始<代码>:s///正在这样做。更复杂的命令现在运行良好:

ls >/dev/null 2>&1; var=$(echo "!#:s/;//") && print "The previous command was:" \'$var\'
给出输出:

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'
但是请注意,在这种情况下,
#
不应与引号一起出现在第一个命令中,因为它将被解释为注释

注1

这里重要的是
#
表示当前行中除当前原子之外的所有内容,因此表达式中的
var

echo "This is command #1"; var=$(echo !#) && echo "The previous command was:" \'$var\'
实际上等于:

var=$(echo echo "This is command #1";)
因此,
echo$var
打印
echo这是前面提到的命令#1
。 但如果我们写的是简化版,没有变量:

echo "This is command #1"; echo "The previous command was:" !#
然后
#
将包含附加的第二个
echo
及其参数,因此实际上命令将变成:

回显“这是命令#1”;“echo”前面的命令是:“echo”这个命令 是命令#1”;echo“上一个命令是:

注2

这个解决方案显然并不完美,因为正如您可能注意到的,变量中不包含引号。您可以尝试在
周围添加单引号来解决此问题#但不能直接(否则历史扩展将无法工作)

注3


如果一行中有3个或3个以上的命令,用
分隔,比您需要使用zsh修饰符或外部命令(如sed或awk)玩得更多一些。

“目前为止,除了当前原子之外,当前行中存在的所有内容”:zsh中的原子是什么?这在医生里吗?顺便说一句,doc指的是一个词,没有明确的定义(我只是猜测)。
Atom
只是一个我认为最能描述我所想的词。关键是
#
直到
才完全收集键入的所有内容#-您可以使用
var=“!#”
var=“
在历史扩展后将不会出现。我不知道如何更好地命名它,但是比较两个命令的结果:
echo foo!
echo”foo#“
。第一个原子包含3个原子(
echo
foo
!#
),但第二个原子只有两个(
echo
“foo!#”
).我在回答中编辑了注释1,希望它现在更清晰。你知道它是按规范编写的还是这是一种实现效果?这是为了知道它是否可靠。从zsh手册中,我猜它是按规范编写的:
该行被视为是完整的,并且包含了带“!#”的单词前面的单词。
。它们显然是c所有单词都是我称之为atom的。但是请记住,例如bash的工作方式不同,所以
echo“foo#“
扩展到
echo”foo echo“foo”
并不会产生任何结果,因为引号不匹配。很好,这正是我需要的!我想修复zsh的好的
旧警报
别名