考虑这样的脚本:
$ cat example.sh
#! /usr/bin/env bash
for i in {1..90}
do
printf '%s\n' "$i"
done
sleep 10
printf '91\n'
sleep 10
printf 'done\n'
假设输出通过管道传输到 less,如下所示:
$ bash example.sh | less
如果我向下滚动到第 90 行,我可以再次向上滚动、搜索并使用less
提供的任何其他交互式命令。然而,一旦我尝试使用 例如j
或越过第 90 行Ctrl-N
,
less
就会停止响应交互式命令,直到另一行输入可用。如果我尝试使用空格键滚动整页超过第 90 行,
less
则会停止响应交互式命令,直到整页输入可用或收到 EOF。
如果我想查看以前的输出并且没有意识到我刚刚浏览了可用行并且必须等待更多行出现(这可能需要任意时间),那么这可能是不可取的。
如果我使用 Ctrl-C 发送 SIGINT,我可以立即再次获得交互性,但随后less
将停止侦听来自管道的更多输入。
该脚本只是一个易于重现的示例,但它可以被任何缓慢生成输出行的长时间运行的命令替换,例如查找损坏的符号链接:
$ find $HOME -xtype l | less
或我的主目录中的世界可读权限:
$ find $HOME -perm 777 | less
或任何其他缓慢的、资源密集型的命令,将行发送到stdout
.
有什么方法可以告诉我less
停止等待更多输入并重新获得交互式命令,而无需等待从管道生成所需的输入行?
答案1
在
bash example.sh | less
整个管道都放在前台(前台进程组既有正在运行的进程bash
,也有它自己生成的所有进程,和less
),因此当您按Ctrl+时C,所有bash
和sleep
都会less
收到 SIGINT。
sleep
收到它后就会死,所以也会bash
死。less
拦截 SIGINT 并将其处理为取消当前操作。
但如果bash
没有死的话,你还是可以在里面继续阅读的less
。
所以你能做的就是防止bash
SIGINT 导致死亡:
(trap '' INT; bash example.sh) | less
然后,您将能够使用Ctrl+C来中断less
,而不会影响bash
该脚本启动的进程。
要停止脚本,您可以使用Ctrl+\发送 SIGQUIT(请注意,如果您未将核心转储大小限制设置为 0,可能会导致核心转储),或使用Ctrl+Z暂停作业 (SIGTSTP),然后将kill %
其终止(使用 SIGTERM)。
1 此处,正如所bash
意识到的那样sleep
,死于 SIGINT;如果处理了该 SIGINT 并正常退出,情况会有所不同,这是由其他一些 shellsleep
完成的特殊 SIGINT 处理。bash
答案2
如果我正确理解你的问题,你期望的行为是中断睡眠并返回到类似交互的vim
模式:
。
答案在于如何处理睡眠功能,因为它sleep
本身不是一个常规的 shell 命令,而是一个非常特殊的命令,它创建一个在特定时间执行的虚拟作业,因此必须考虑到这一点中断处理。但是,您的行为可以通过修改脚本轻松实现。
#!/bin/bash
trap 'kill $(jobs -p)' INT
for i in {1..90}
do
printf '%s\n' "$i"
done
sleep infinity &
wait
printf '91\n'
sleep 10 &
printf 'done\n'
我对您的原始脚本进行了一些修改,以便它可以无限休眠以进行测试。
当您到达并尝试越过第 90 行时,系统sleep
将永远运行。要退出它只需点击CTRL+C,由于信号陷阱,这不仅会中断我们的sleep
工作,还会返回所需的内容:
及其功能(执行后您可能需要按一个键CTRL+C)。此外,一旦您再次尝试移过第 90 行,将立即打印第 91 行,因为脚本执行已移过第一个sleep
作业。
答案3
最不糟糕的解决方案是使用setsid(1)
:
setsid bash example.sh | less
或者
setsid find $HOME -xtype l | less
或者
setsid find $HOME -perm 777 | less
“最不坏”既因为setsid(1)
作为一个命令是非标准的 Linux 主义,所以如果它尚不可用,你需要从最近的包管理器安装它(事实上它是可移植的),而且因为有问题的命令也会逃脱作业控制,所以如果它得到真的卡住了,你别无选择,只能找到它ps x
并使用找到的 PID 手动使用kill命令。
还有其他选择,但它们都有自己的便携性或人体工程学问题。比照:https://unix.stackexchange.com/a/736048/54340