zsh可以访问上次运行程序的标准输出吗?


15

我经常使用findlocate查找有关路径的信息。

(~) locate foobar.mmpz
/home/progo/lmms/projects/foobar.mmpz

下一步通常是打开文件或以其他方式操作文件。在像上面这样的快乐情况下,我可以这样做:

(~) ls `!!`
ls `locate foobar.mmpz`
/home/progo/lmms/projects/foobar.mmpz

但是,当有许多行输出时,没有人会太高兴,其中某些行可能不是路径或其他类似的东西。此外,重新运行可能浪费的命令也不是一件容易的事。

是否有办法连接zsh将stdout存储到数组中以供以后处理?毕竟,将流重定向到用户是Shell的工作。我在想它可以将前N行和后N行存储在变量中,以便像$?其他变量一样立即使用。

好的,这很酷:https : //unix.stackexchange.com/a/59704/5674。我现在正在询问有关zsh的专门知识(并将代码移植到zsh),以便在每次运行后都进行这种捕获。


将流重定向到用户并不是shell的工作。应用程序将其输出写入终端设备,外壳不参与其中。
斯特凡Chazelas

@StephaneChazelas,但这是shell的工作,例如,根据用户的请求将流重定向到文件中。也有zsh的配方,将stderr染成红色以将其与stdout分开。我认为这表明Shell在流媒体问题中的作用。
unperson325680

是的,您可以使用 screenscript和precmd和preexec挂钩来实现。
斯特凡Chazelas

Answers:


7

在大多数终端仿真器上,没有捕获屏幕输出的功能。我似乎记得xterm(“参考”终端仿真器)的作者说过,这将很难实现。即使可能,外壳程序也必须跟踪最后一个提示的位置。

因此,除非您使用特定于终端的手动机制,例如通过xterm中的鼠标或Screen中的键盘进行复制粘贴,否则您不必逃避再次运行该命令的麻烦。

对于shell来说,自动捕获命令的输出是非常不切实际的,因为它无法区分具有复杂的终端和用户交互的命令与仅输出可打印字符的命令。

您可以重新运行命令并捕获其输出。每种方法有很多种。要重新运行该命令,可以使用:

  • !! 历史记录替换-最方便键入;
  • fc -e -,可以在函数中使用。

要捕获输出,可以使用命令替换或类似以下的功能:

K () {
  lines=("${(f@)$(cat)}")
}
!! |K

lines会将数组设置为通过管道传递给它的命令的输出。


随它吧。现在,根据良好的解释,我意识到完全自动化的方法实在太困难了,第二好的事情就是K您自己的东西。
unperson325680

3

这是将内容的最后一行放入名为的变量中的第一步$lastline

precmd () {                                                                                                                                                                                                        
    exec 2>&- >&-
    lastline=$(tail -1 ~/.command.out) 
    sleep 0.1   # TODO: synchronize better
    exec > /dev/tty 2>&1
}

preexec() {
    exec > >(tee ~/.command.out&)
}

这使用zsh的preexec钩子exectee存储命令的stdout的拷贝,然后使用precmd以读取所存储的输出和恢复标准输出是公正的终端用于显示提示。

但是它仍然需要一些工作。例如,由于stdout不再是终端,因此诸如vim和的程序less无法正常工作。

这些问题中包含一些有用的相关信息:


是的,也许100%自动的方法行不通。exec代码中有很多调用,命令是否多次运行,还是我陷入特殊的语义中?
unperson325680

exec没有程序名称的只是设置重定向。
Mikel 2014年

2

您可以通过将结果传递到来完成此操作,该结果tee仅将输出保存到文件中并同时显示。

因此,例如,您可以执行此操作,该操作可以正常显示您的输出,但也可以将其保存到文件中 /tmp/it

locate foobar.mmpz | tee /tmp/it

然后整理该文件并grep进行选择,例如

cat /tmp/it | grep progo | grep lms

然后使用它,您可以这样做:

vi $(!!)


2

我想出了这个半生半熟的解决方案:

alias -g ___='"$(eval "$(fc -ln -1)" | tail -n 1)"'

这使您可以___在命令行中的任何位置进行编写。先前的命令将重新运行,并且___将用其输出的最后一行替换。用法示例:

$ touch foo bar baz
$ ls -1
bar
baz
foo
$ vim ___

最后一个命令将扩展为vim foo

这确实有一些锋利的边缘!

  • 如果您包含___在命令中,但先前的命令还包含a ___,则shell将以某种奇怪的状态挂起一段时间。您可以使用Ctrl- 立即退出此状态C

  • 您也无法像其他结构一样按Tab展开。___!$

  • 当某些命令“正常”运行并将它们连接到管道时,它们将显示不同的输出。(比较器的输出lsls | cat。)如果触发命令___是其中之一,你可能最终运行速度低于预期不同的命令。

  • 当然,如果您想对输出线进行除最后一条以外的其他操作,这将无济于事。

___之所以选择这个名称,是因为我从未想过将它作为命令行中的单词,甚至不作为参数来包含。您可以选择其他名称,但要注意不要选择可能会无意间为您扩展的名称。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.