等待命令失敗

等待命令失敗

下面的腳本除了說明這個問題之外沒有其他目的。

#!/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)。

相關內容