2011-07-03 38 views
20

我越來越喜歡在shell腳本中的函數之間使用類似生成器的模式。事情是這樣的:通知左側故障管道的右側?

parse_commands /da/cmd/file | process_commands 

然而,這種模式的基本問題是,如果parse_command遇到一個錯誤,我發現的唯一途徑通知process_command它失敗是明確的告訴它(如回聲「FILE_NOT_FOUND」 )。這意味着parse_command中每個潛在的錯誤操作都必須被隔離。

有沒有辦法process_command可以檢測到左側退出非零退出代碼?

+0

一如既往,第二次使用谷歌搜索發現這個http://www.unix.com/302268337-post4.html – Bittrance

+1

http://stackoverflow.com/questions/32698407/kill-next-command-in-pipeline-on-失敗bash/32699218#32699218是一個稍微更清楚的版本,這個問題有可以說是更好的答案(問題是如何通知管道的右側失敗,但接受的答案只解決如何檢測父shell中的情況)。 –

回答

14

即使第一個過程已經結束,管道過程是否仍然繼續,或者是您無法知道第一個過程失敗的問題?

如果是後者,您可以查看PIPESTATUS變量(它實際上是一個BASH數組)。這會給你第一個命令的退出代碼:

parse_commands /da/cmd/file | process_commands 
temp=("${PIPESTATUS[@]}") 
if [ ${temp[0]} -ne 0 ] 
then 
    echo 'parse_commands failed' 
elif [ ${temp[1]} -ne 0 ] 
then 
    echo 'parse_commands worked, but process_commands failed' 
fi 

否則,你將不得不使用協同程序。

+1

這確實提供了這些信息,但它不能在process_command內完成,因此可讀性受損。 – Bittrance

0

如果你有command1 && command2那麼命令2只會在第一個命令成功時執行 - 否則布爾短路開始。使用這種方法的一種方法是建立第一個命令(你的parse_commands...)轉儲到臨時然後從該文件獲取第二個命令。

編輯:通過審慎使用;你可以清理臨時文件,例如:

(command1 && command2) ; rm temporaryfile 
5

不像和操作員(& &),管道操作符(|)的工作原理是同時產卵兩個過程,所以第一個過程可以通過管道將其輸出到第二處理,而不需要緩衝中間數據。這允許用很少的內存或磁盤使用來處理大量的數據。

因此,第一個進程的退出狀態在第二個進程完成之前不可用。

+0

不一定正確。例如,請參閱http://stackoverflow.com/a/32699218/14122 –

+1

@Charles我仍然會說,一旦退出,您只能獲得第一個進程的退出狀態。但實際上,根據包括你在內的各種答案,使用FIFO或PIPESTATUS聽起來像是OP問題的多功能解決方案,可以以適當的方式訪問退出狀態。 – jjmontes

4

你可以嘗試一些工作以防萬一使用FIFO:

mkfifo /tmp/a 
cat /tmp/a | process_commands & 

parse_cmd /da/cmd/file > /tmp/a || (echo "error"; # kill process_commands) 
+0

是的,我認爲這是關於最好的,你可以做,而不寫一個C程序,大致相同的事情,只有一個普通的管道,而不是一個命名管道。我想知道zsh coproc設施會對此有所幫助,但我並不十分了解它。 – andrewdski

0

有一個辦法在bash 4.0,從灰添加coproc內建做到這一點。這個協處理設施是從ksh借用的,它使用了不同的語法。我在我的系統上支持coprocesses的唯一shell是ksh。這裏是KSH寫了一個解決方案:

parse_commands /da/cmd/file |& 
parser=$! 

process_commands <&p & 
processor=$! 

if wait $parser 
then 
    wait $processor 
    exit $? 
else 
    kill $processor 
    exit 1 
fi 

的想法是與它連接到主殼管後臺啓動parse_commands。該pid保存在parser。然後process_commandsparse_commands的輸出作爲輸入開始。 (這是<&p所做的。)這也放在背景中,它的pid保存在processor

這兩個在後臺連接的管道,我們的主要shell可以自由地等待解析器終止。如果它沒有錯誤而終止,我們等待處理器完成並退出並返回代碼。如果它終止出錯,我們殺死處理器並以非零狀態退出。

它應該是相當直接的翻譯這使用bash 4.0/ash coproc內置,但我沒有很好的文檔,也沒有一種方法來測試。

+0

如何確保'process_commands'在獲取信號前不會退出EOF(當FIFO的寫入結束時關閉)? –

0

您可以運行一個明確的子shell parse_commands /da/cmd/fileecho子shell通過管道的退出狀態process_commands這也是在一個明確的子shell運行處理包含在/dev/stdin的管道數據。

遠不是優雅,但似乎完成這項工作:)

一個簡單的例子:

(
(ls -l ~/.bashrcxyz; echo $?) | 
( 
piped="$(</dev/stdin)"; 
[[ "$(tail -n 1 <<<"$piped")" -eq 0 ]] && printf '%s\n' "$piped" | sed '$d' || exit 77 
); 
echo $? 
) 
0

什麼:

parse_commands /da/cmd/file > >(process_commands) 
+0

不在這裏。用'false'>(/ bin/echo foo)測試' – xebeche

28

在你的bash頂端使用set -o pipefail腳本,以便在管道左側失效時(退出狀態!= 0),右側不執行。

+7

「set -o pipefail」不會阻止右側被執行,所以它不會回答最初的問題,即「如何通知右側...「基本上這會改變管道的退出狀態:默認情況下,管道的退出狀態是右側的退出狀態,使用此選項它是管道命令的最大值。 – mcoolive

+3

但這是我正在尋找的:-)所以它可以幫助我。 Thx – mcoolive

+0

這會使它看起來像整個命令失敗? – dtracers

2

我沒有足夠的信譽發表評論,但accepted answer是缺少一個右}第5行

固定在此之後,該代碼將拋出一個-ne: unary operator expected錯誤,它指向一個問題:PIPESTATUS是被if命令後的條件覆蓋,所以返回值process_commands將永遠不會被檢查!

這是因爲[ ${PIPESTATUS[0]} -ne 0 ]equivalent totest ${PIPESTATUS[0]} -ne 0,它與其他任何命令一樣更改$PIPESTATUS。例如:

return0() { return 0;} 
return3() { return 3;} 

return0 | return3 
echo "PIPESTATUS: ${PIPESTATUS[@]}" 

如預期的那樣返回PIPESTATUS: 0 3。但是如果我們引入條件呢?

return0 | return3 
if [ ${PIPESTATUS[0]} -ne 0 ]; then 
    echo "1st command error: ${PIPESTATUS[0]}" 
elif [ ${PIPESTATUS[1]} -ne 0 ]; then 
    echo "2nd command error: ${PIPESTATUS[1]}" 
else 
    echo "PIPESTATUS: ${PIPESTATUS[@]}" 
    echo "Both return codes = 0." 
fi 

我們拿到的[: -ne: unary operator expected錯誤,並且這樣的:

PIPESTATUS: 2 
Both return codes = 0. 

爲了解決這個問題,$PIPESTATUS應存放在不同的數組變量,像這樣:

return0 | return3 
TEMP=("${PIPESTATUS[@]}") 
echo "TEMP: ${TEMP[@]}" 
if [ ${TEMP[0]} -ne 0 ]; then 
    echo "1st command error: ${TEMP[0]}" 
elif [ ${TEMP[1]} -ne 0 ]; then 
    echo "2nd command error: ${TEMP[1]}" 
else 
    echo "TEMP: ${TEMP[@]}" 
    echo "All return codes = 0." 
fi 

它打印:

TEMP: 0 3 
2nd command error: 3 
按照預期

編輯:我確定了接受的答案,但是我將這個解釋留給後代。