我正在尝试弄清如何执行最懒惰的标准UNIX shell管道处理。例如,假设我有一个命令,它在进行一些计算和输出的同时,计算变得越来越昂贵,因此前几行输出很快到达,但随后的行变得越来越慢。如果我只对前几行感兴趣,则可以通过惰性求值获得这些行,在计算变得太昂贵之前尽快终止计算。
这可以通过一个直接的shell管道实现,例如:
然而,这种方法并不是最优的。让我们使用一个逐渐变慢的脚本来模拟计算过程:
所以问题是,我该如何让
这可以通过一个直接的shell管道实现,例如:
./expensive | head -n 2
然而,这种方法并不是最优的。让我们使用一个逐渐变慢的脚本来模拟计算过程:
#!/bin/sh
i=1
while true; do
echo line $i
sleep $(( i ** 4 ))
i=$(( i+1 ))
done
现在我将此脚本通过head -n 2
进行管道传输,观察到以下结果:
- 输出
line 1
。 - 等待一秒后,输出
line 2
。 - 尽管
head -n 2
已经接收到两个(以\n
结尾的)行并退出,但expensive
仍继续运行,并且现在在完成之前等待进一步的16秒(即2 ** 4
),此时管道也随之完成。
所以问题是,我该如何让
expensive
在head
退出时立即退出,而不仅仅是当expensive
试图将其第三行写入不再具有侦听器的管道时?由于管道是由我键入./expensive | head -n 2
命令的交互式shell进程构建和管理的,因此解决此问题的任何解决方案都应该位于交互式shell中,而不是对expensive
或head
进行任何修改。是否有任何本地技巧或额外实用程序可以构建我想要的行为的管道?或者也许在bash
或zsh
中无法实现我想要的内容,唯一的方法是编写自己的管道管理器(例如使用Ruby或Python),它可以在读取器终止并立即终止写入器时发现这一点?
.sh
扩展名来命名 shell 库,这些库可以在任何符合 POSIX 标准的 shell 中被引用(.bash
用于仅与 bash 兼容的库,.zsh
用于与 zsh 兼容的库)。将扩展名用于可执行命令会在将它们重写为不同语言时带来麻烦——现在您需要更新每个调用者以调用一个不同命名的命令,或者您有一个具有误导性名称的脚本——而且,使用bash
shebang 调用脚本并给它一个暗示sh
可以调用它的名称是具有误导性的。 - Charles Duffy$(( ))
是bash
特有的,而不是符合POSIX标准的? - Adam Spiers$(( ))
是符合 POSIX 标准的——只是使用(( ))
进入算术上下文而不进行替换的结果是一个 bashism——但#!/bin/bash
的 shebang 意味着根据调用方式,你会得到两个不同的解释器(或以不同模式运行的解释器)。 - Charles Duffy