在less(1) 中终止文本搜索并继续通过Unix 管道读取

nod*_*kai 5 less pipe

gunzip < 100terabytes.txt.gz | less
Run Code Online (Sandbox Code Playgroud)

less(1)根据需要读取尽可能多的行以填满您的屏幕,然后停止到read(2). 结果,当管道变满时gunzip(1)被阻塞write(2)

当您向下滚动时,less(1)问题read(2)一次又一次,并且随着管道被消耗,gunzip(1)允许发出write(2). 您可以完全灵活地在此处来回切换(假设gunzip < 100terabytes.txt.gz未完成。)

到目前为止一切都很好。

你可以开始在文本搜索中less(1)使用/。但是当在 中找不到搜索字符串时100terabytes.txtless(1)基本上变得无响应。您可以使用 终止搜索Ctrl-C它似乎关闭了gunzip(1)和之间的管道less(1)。我不喜欢这个。gunzip(1)在我放弃文本搜索后,我想手动向下滚动以消耗更多行。这可能吗?

不是在寻求类似的建议gunzip < 100terabytes.txt.gz | grep pattern | less

更新

你可以尝试 od -v /dev/zero | less

Sté*_*las 4

当您按下 时Ctrl+C,整个 shell作业(进程组)都会接收 SIGINT,less拦截它以中止搜索,但gunzip会终止。为了避免这种情况,你可以这样做:

(trap '' INT; gunzip < file.gz) | less
Run Code Online (Sandbox Code Playgroud)

这样就忽略了 SIGINT,但请注意,此后gunzip您将无法再中断。gunzip

对于gunzip,这可能没问题,因为您需要做的就是退出,less下次gunzip它写入某些内容时就会因 SIGPIPE 而死亡,但对于只是挂起而不输出某些内容的应用程序,这将是一个更大的问题(您仍然会不过可以用于Ctrl+ZSIGTSTP 或Ctrl+\SIGQUIT)。

另请注意,某些命令例如pvping安装它们自己的 SIGINT 处理程序,这将恢复我们的trap '' INT.

您可以创建一个函数来保存输入,例如:

iless() {
  (trap '' INT; "$@") | less
}

iless gunzip < file.gz
Run Code Online (Sandbox Code Playgroud)

或者:

noint() (trap '' INT; "$@")

noint gunzip < file.gz | less
Run Code Online (Sandbox Code Playgroud)

但请注意,对于:

gunzip < file.gz | grep foo | less
Run Code Online (Sandbox Code Playgroud)

你需要写它:

noint gunzip < file.gz | notint grep foo | less
Run Code Online (Sandbox Code Playgroud)

或者:

noint eval 'gunzip < file.gz | grep foo' | less
Run Code Online (Sandbox Code Playgroud)

或者:

iless eval 'gunzip < file.gz | grep foo'
Run Code Online (Sandbox Code Playgroud)

另一种方法是使用进程替换:

less -f <(gunzip < file.gz | grep foo)
Run Code Online (Sandbox Code Playgroud)

或(尽管不在 中zsh):

less < <(gunzip < file.gz | grep foo)
Run Code Online (Sandbox Code Playgroud)

在这些情况下,shell 不会在前台进程组中的进程替换中包含命令(除了 for 的第二种情况zsh)。它们的进程组与 shell 的进程组相同。这意味着当您按 时他们不会收到 SIGINT Ctrl+C

请注意,这些进程不会受到Ctrl+ZCtrl+\的影响。

zshksh93和中进行了测试bash