等待命令失败

等待命令失败

下面的脚本除了说明这个问题之外没有其他目的。

#!/usr/bin/env zsh

arbitrary_pipeline () {
    shuf | tr a-z A-Z
}

tmpdir=$( mktemp -d )

mkfifo $tmpdir/{orig,alt}

{ tee $tmpdir/orig | arbitrary_pipeline > $tmpdir/alt; } &
pid=$!

paste $tmpdir/orig $tmpdir/alt

rm -rf $tmpdir

wait $pid

该脚本使用tee两个命名管道将一些(任意)标准输入拆分为两个流,将其中之一重定向到任意管道,并将生成的两个流的输入传递给paste.示意图:

STDIN --- > --.- arbitrary_pipeline -.
               \                      \
          paste `----<FIRST-ARGUMENT>  `- <SECOND-ARGUMENT> --> STDOUT

(在这种情况下,arbitrary_pipeline只是打乱其标准输入,并将其转换为大写,但是,顾名思义,它可以是任何东西。)

脚本的 stdout 输出看起来不错,但wait命令总是失败:

% grep -iP 'z.*s.*h' /usr/share/dict/words | /tmp/test.sh
Nietzsche   CITIZENSHIP'S
Zubeneschamali  NIETZSCHE
Zubeneschamali's    ZUBENESCHAMALI
citizenship CITIZENSHIP
citizenship's   ZUBENESCHAMALI'S
/tmp/test.sh:wait:18: pid 26357 is not a child of this shell

我究竟做错了什么?


前言:

/usr/bin/env zsh --version
# zsh 5.0.7 (x86_64-pc-linux-gnu)

编辑:

  1. tee根据乔丹的建议,在管道周围添加了大括号。 (不过结果没有改变。)
  2. 替换&!&响应 Stéphane Chazelas 的评论。 (同样,结果没有改变。)

答案1

在 5.0.8 之前,zsh已经等不及已经死掉的工作了。 2014 年 5.0.8 中对此进行了更改。请参阅更改那里

在这里,您可以将 stderr 重定向到/dev/null忽略该问题:

wait $pid 2> /dev/null

请注意,在:

{ tee $tmpdir/orig | arbitrary_pipeline > $tmpdir/alt; } &

作为一种优化,zsh不会为 分叉额外的进程arbitrary_pipeline,它将在与运行在后台启动的子 shell 的进程相同的进程中执行它。

paste在它的 stdin 上看到 EOF 之前不会完成,它的 stdin 是$pid在另一端写入的管道(及其子项,如果有)。因此,直到$pid(和子级)将其所有文件描述符(通常只有 stdout)关闭到管道的写入端之前,它不会看到 eof 。除非$pid明确关闭其标准输出(这是非常不常见的),否则只有在退出时才会发生。

这意味着paste在大多数情况下,不会在此之前退出,以防万一$pid仍然是一个好主意。wait

请注意,在这里,您可以使用 acoproc来避免临时 fifo:

coproc arbitrary_pipeline
cat >&p | paste - /dev/fd/3 3<&p &
coproc : close
wait

(注意,wait还要等待 s coproc)。

相关内容