【问题标题】:Is there a way to abort a piped shell command?有没有办法中止管道 shell 命令?
【发布时间】:2020-05-04 08:47:15
【问题描述】:

使用这个命令:

$ tar -ztf /tmp/z.txz | grep -m 1 '/packagesite.txz$'
2.2.0/FreeBSD:11:amd64/latest/packagesite.txz

结果来得非常快(不到 1 秒),但随后它会继续运行 10-15 秒以上的无用秒。 I found out about -m here,但没有帮助。如果我能在找到匹配项后立即中止它,那就太好了。有什么办法吗?仅供参考,必须在 /bin/sh 工作。

【问题讨论】:

  • 您是否附加了头部-1?焦油-ztf /tmp/z.txz | grep -m 1 '/packagesite.txz$' |头 -1
  • @WaltDe - 不管有没有| head -1

标签: grep sh tar


【解决方案1】:

到目前为止,没有任何东西是有效的,但我注意到this post 中有一些有趣的东西:

find . ... -exec sh -c 'printf "%s\n" "$1"; kill "$PPID"' sh {} \;

以及this post 中有关管道和进程组的更多信息:

如果pid等于0,则sig被发送到调用进程的进程组中的每个进程。

所以我开始玩,结果发现它确实奏效了:

$ time tar -ztf /tmp/z.txz | grep -m1 '/packagesite\.txz$'
2.2.0/FreeBSD:11:amd64/latest/packagesite.txz
53.866 real, 3.947 user, 5.119 sys
$
$ time tar -ztf /tmp/z.txz | ( grep -m1 '/packagesite\.txz$' ; kill 0 )
2.2.0/FreeBSD:11:amd64/latest/packagesite.txz
Terminated
0.008 real, 0.003 user, 0.004 sys

起初我担心会出现“终止”文本,但它不在标准输出上,因此不会妨碍:

$ tar -ztf /tmp/z.txz | ( grep -m1 '/packagesite\.txz$' ; kill 0 ) > z.out
Terminated
$ cat z.out
2.2.0/FreeBSD:11:amd64/latest/packagesite.txz

编辑:

但是,我确实注意到,此解决方案会弄乱状态代码,例如:

$ tar -ztf /home/mrengert/tmp/z.txz | ( grep -m1 '/packagesite\.txz$' ; kill 0 )
2.2.0/FreeBSD:11:amd64/latest/packagesite.txz
Terminated
$ echo $?
143

另外 - 如果你在脚本中使用它,你必须小心防止信号传播,否则它将终止脚本。你需要做这样的事情:

#!/bin/sh
trap '' SIGTERM  # ignore signal temporarily
( trap - SIGTERM ; tar -ztf z.txz | ( grep -m1 '/packagesite\.txz$' ; kill 0 ) )
trap - SIGTERM   # restore signal handling

【讨论】:

  • 如果你包装成(cmd) || true,你会在最后得到状态码0。
  • @kenorb - ...是的,但是如果有一个真正的问题(例如 tar 得到一个错误的存档),你就会错过它。我想我会明确检查[ $? -ne 0 -a $? -ne 143 ] BTW,143 是 128 +(无论什么杀死代码,例如 SIGTERM = 15)。
【解决方案2】:

您的问题与缓冲有关。这是一种更简单的复制方法。

perl -le 'for (1..1000) { print "$_"; sleep 1 }' | grep -m 1 4

似乎什么都没有发生,即使您可以通过取出grep 来验证第四行输出是否匹配。

从 Perl 关闭缓冲解决了直接的问题:

perl -le '$| = 1; for (1..1000) { print "$_"; sleep 1 }' | grep -m 1 4
4

(可以预见,输出会在 4 秒后出现)。

对此没有完全可移植的解决方案;看看你是否有https://mywiki.wooledge.org/BashFAQ/009 中描述的实用程序之一,或者确保tar 产生足够的输出,你可以依靠输出缓冲区填满。

【讨论】:

  • 我尝试使用stdbuf -oL,如您提供的链接中所示(以及许多其他方式),但没有任何区别。我发现了一些有用的东西 - 请参阅我的答案。
【解决方案3】:

您的文件可能很大或者您的存储设备速度很慢。 Grep 将过滤结果并停止读取管道,但不会终止管道命令。

如果您知道结果会在 1 秒后显示,至于解决方法,您可以在此之后使用timeout 杀死 shell 命令。例如:

timeout 1 tar -ztf /tmp/z.txz | grep -m1 '/packagesite.txz$'

【讨论】:

  • 谢谢,但我不确定需要多长时间。我发现了一些有用的东西 - 请参阅我的答案。
【解决方案4】:

是的,它不能按预期工作,根据我的发现和引用 grep man 页面,它仅适用于文件。

以下是来自man 页面的 sn-p:

-m NUM, --max-count=NUM

在 NUM 个匹配行之后停止读取文件。如果输入是 来自常规文件的标准输入,并且 NUM 匹配行是 输出,grep 确保标准输入被定位到 在退出之前的最后一个匹配行之后,无论 尾随上下文行的存在。这启用了调用进程 恢复搜索。当 grep 在 NUM 个匹配行之后停止时,它 输出任何尾随上下文行。当 -c 或 --count 也使用选项,grep 不输出大于 NUM 的计数。 当同时使用 -v 或 --invert-match 选项时,grep 会在 正在输出 NUM 个不匹配的行。

【讨论】:

    猜你喜欢
    • 2015-05-11
    • 2018-12-10
    • 2013-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-12
    • 2016-06-26
    相关资源
    最近更新 更多