मेरी दो प्रक्रियाएँ हैं fooऔर bar, एक पाइप से जुड़ी हुई हैं:
$ foo | bar
barहमेशा बाहर निकलता है 0; मुझे बाहर निकलने के कोड में दिलचस्पी है foo। क्या इसे पाने का कोई तरीका है?
मेरी दो प्रक्रियाएँ हैं fooऔर bar, एक पाइप से जुड़ी हुई हैं:
$ foo | bar
barहमेशा बाहर निकलता है 0; मुझे बाहर निकलने के कोड में दिलचस्पी है foo। क्या इसे पाने का कोई तरीका है?
जवाबों:
यदि आप उपयोग कर रहे हैं bash, तो आप PIPESTATUSपाइपलाइन के प्रत्येक तत्व की निकास स्थिति प्राप्त करने के लिए सरणी चर का उपयोग कर सकते हैं ।
$ false | true
$ echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"
1 0
यदि आप उपयोग कर रहे हैं zsh, तो उन्हें सरणी कहा जाता है pipestatus(मामले मायने रखता है!) और सरणी सूचकांक एक पर शुरू होते हैं:
$ false | true
$ echo "${pipestatus[1]} ${pipestatus[2]}"
1 0
एक फ़ंक्शन के भीतर उन्हें संयोजित करने के लिए जो मानों को नहीं खोता है:
$ false | true
$ retval_bash="${PIPESTATUS[0]}" retval_zsh="${pipestatus[1]}" retval_final=$?
$ echo $retval_bash $retval_zsh $retval_final
1 0
ऊपर चलाने के लिए bashया zshआप एक ही परिणाम प्राप्त करेंगे; केवल एक retval_bashऔर retval_zshसेट किया जाएगा। दूसरा खाली होगा। यह एक फ़ंक्शन को समाप्त करने की अनुमति देगा return $retval_bash $retval_zsh(उद्धरण की कमी पर ध्यान दें!)।
pipestatuszsh में। दुर्भाग्य से अन्य गोले में यह सुविधा नहीं है।
echo "$pipestatus[1]" "$pipestatus[2]"।
if [ `echo "${PIPESTATUS[@]}" | tr -s ' ' + | bc` -ne 0 ]; then echo FAIL; fi
ऐसा करने के 3 सामान्य तरीके हैं:
पहला तरीका pipefailविकल्प ( ksh, zshया bash) सेट करना है । यह सबसे सरल है और यह जो करता है वह मूल रूप से $?गैर-शून्य (या शून्य यदि सभी सफलतापूर्वक बाहर निकलता है) से बाहर निकलने के लिए अंतिम कार्यक्रम के निकास कोड के लिए निकास की स्थिति निर्धारित करता है ।
$ false | true; echo $?
0
$ set -o pipefail
$ false | true; echo $?
1
बैश में एक सरणी चर भी होता है जिसे $PIPESTATUS( $pipestatusमें zsh) कहा जाता है जिसमें अंतिम पाइपलाइन में सभी कार्यक्रमों की निकास स्थिति होती है।
$ true | true; echo "${PIPESTATUS[@]}"
0 0
$ false | true; echo "${PIPESTATUS[@]}"
1 0
$ false | true; echo "${PIPESTATUS[0]}"
1
$ true | false; echo "${PIPESTATUS[@]}"
0 1
आप पाइप लाइन में विशिष्ट मूल्य प्राप्त करने के लिए तीसरे कमांड उदाहरण का उपयोग कर सकते हैं जिसकी आपको आवश्यकता है।
यह समाधानों का सबसे अधिक वर्णन है। प्रत्येक कमांड को अलग से चलाएं और स्टेटस कैप्चर करें
$ OUTPUT="$(echo foo)"
$ STATUS_ECHO="$?"
$ printf '%s' "$OUTPUT" | grep -iq "bar"
$ STATUS_GREP="$?"
$ echo "$STATUS_ECHO $STATUS_GREP"
0 1
ksh, लेकिन इसके संक्षिप्त पृष्ठ पर संक्षिप्त नज़र से देखें, तो यह समर्थन $PIPESTATUSया कुछ भी समान नहीं है। pipefailहालांकि यह विकल्प का समर्थन करता है ।
LOG=$(failed_command | successful_command)
यह समाधान बैश विशिष्ट सुविधाओं या अस्थायी फ़ाइलों का उपयोग किए बिना काम करता है। बोनस: अंत में बाहर निकलने की स्थिति वास्तव में एक निकास स्थिति है और फ़ाइल में कुछ स्ट्रिंग नहीं है।
परिस्थिति:
someprog | filter
आप से बाहर निकलने की स्थिति someprogऔर से उत्पादन चाहते हैं filter।
यहाँ मेरा समाधान है:
((((someprog; echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1
इस निर्माण का परिणाम, निर्माण के स्टडआउट के filterरूप में और निर्माण की स्थिति से बाहर निकलने की स्थिति से बाहर निकलने की स्थिति से स्टडआउट है someprog।
यह निर्माण {...}उपसमूह के बजाय साधारण कमांड ग्रुपिंग के साथ भी काम करता है (...)। सबस्क्रिप्शन के कुछ निहितार्थ हैं, दूसरों के बीच एक प्रदर्शन लागत, जिसकी हमें यहाँ आवश्यकता नहीं है। अधिक जानकारी के लिए ठीक बैश मैनुअल पढ़ें: https://www.gnu.org/software/bash/manual/html_node/Command-Grouping.html
{ { { { someprog; echo $? >&3; } | filter >&4; } 3>&1; } | { read xs; exit $xs; } } 4>&1
दुर्भाग्य से बैश व्याकरण में घुंघराले ब्रेसिज़ के लिए रिक्त स्थान और अर्धविराम की आवश्यकता होती है ताकि निर्माण अधिक विस्तृत हो जाए।
इस पाठ के बाकी हिस्सों के लिए मैं उप-संस्करण का उपयोग करूंगा।
उदाहरण someprogऔर filter:
someprog() {
echo "line1"
echo "line2"
echo "line3"
return 42
}
filter() {
while read line; do
echo "filtered $line"
done
}
((((someprog; echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1
echo $?
उदाहरण आउटपुट:
filtered line1
filtered line2
filtered line3
42
नोट: चाइल्ड प्रोसेस पैरेंट से ओपन फाइल डिस्क्रिप्टर इनहेरिट करता है। इसका मतलब है कि someprogओपन फाइल डिस्क्रिप्टर 3 और 4 को इनहेरिट किया जाएगा। अगर आप someprogडिस्क्रिप्टर 3 फाइल करने के लिए लिखते हैं तो वह एक्ज़िट स्टेटस बन जाएगा। वास्तविक निकास स्थिति को अनदेखा किया जाएगा क्योंकि readकेवल एक बार पढ़ता है।
यदि आप चिंता करते हैं कि आपका someprogविवरण 3 या 4 फाइल करने के लिए लिख सकता है, तो कॉल करने से पहले फाइल डिस्क्रिप्टर को बंद करना सबसे अच्छा है someprog।
(((((exec 3>&- 4>&-; someprog); echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1
exec 3>&- 4>&-पहले someprogनिष्पादित करने से पहले फ़ाइल वर्णनकर्ता बंद कर देता है someprogके लिए बहुत someprogउन फ़ाइल वर्णनकर्ता बस मौजूद नहीं हैं।
इसे इस तरह भी लिखा जा सकता है: someprog 3>&- 4>&-
कंस्ट्रक्शन के स्टेप बाई स्टेप स्पष्टीकरण:
( ( ( ( someprog; #part6
echo $? >&3 #part5
) | filter >&4 #part4
) 3>&1 #part3
) | (read xs; exit $xs) #part2
) 4>&1 #part1
बढ़ना:
#part3) और दाएं ( #part2) पर कमांड निष्पादित होते हैं। exit $xsपाइप की अंतिम कमांड भी है और इसका मतलब है कि स्टड से स्ट्रिंग पूरे निर्माण की निकास स्थिति होगी।#part2और बदले में पूरे निर्माण की निकास स्थिति होगी।#part5और #part6) और दाएं ( filter >&4) पर कमांड निष्पादित होते हैं। filterफ़ाइल डिस्क्रिप्टर 4 में आउटपुट को रीडायरेक्ट किया #part1गया है। फ़ाइल डिस्क्रिप्टर 4 में स्टडआउट पर रीडायरेक्ट किया गया था। इसका मतलब है कि आउटपुट filterपूरे निर्माण का आधार है।#part6फाइल डिस्क्रिप्टर 3 से एक्ज़िट स्टेटस प्रिंट #part3किया जाता है #part2। फाइल डिस्क्रिप्टर 3 में रीडायरेक्ट किया गया था । इसका मतलब यह है कि बाहर #part6निकलने की स्थिति पूरे निर्माण के लिए अंतिम निकास स्थिति होगी।someprogनिष्पादित किया जाता है। बाहर निकलने की स्थिति में लिया जाता है #part5। स्टडआउट को पाइप द्वारा अंदर ले जाया जाता है #part4और आगे भेजा जाता है filter। filterजैसा कि इसमें बताया गया है, से आउटपुट स्टडआउट तक पहुंच जाएगा#part4(read; exit $REPLY)
(exec 3>&- 4>&-; someprog)को सरल करता है someprog 3>&- 4>&-।
{ { { { someprog 3>&- 4>&-; echo $? >&3; } | filter >&4; } 3>&1; } | { read xs; exit $xs; }; } 4>&1
जबकि वास्तव में आपने जो पूछा था, आप उसका उपयोग नहीं कर सकते
#!/bin/bash -o pipefail
ताकि आपके पाइप अंतिम गैर शून्य रिटर्न लौटाएं।
कोडिंग थोड़ी कम हो सकती है
संपादित करें: उदाहरण
[root@localhost ~]# false | true
[root@localhost ~]# echo $?
0
[root@localhost ~]# set -o pipefail
[root@localhost ~]# false | true
[root@localhost ~]# echo $?
1
set -o pipefailस्क्रिप्ट के अंदर और अधिक मजबूत होना चाहिए, उदाहरण के लिए यदि कोई स्क्रिप्ट के माध्यम से निष्पादित करता है bash foo.sh।
-o pipefailPOSIX में नहीं है।
#!/bin/bash -o pipefail। त्रुटि है:/bin/bash: line 0: /bin/bash: /tmp/ff: invalid option name
#!पहले वाले से आगे लाइनों पर रिक्त स्थान को पार्स नहीं करते हैं , और इसलिए /bin/bash -o pipefail /tmp/ff, आवश्यक के बजाय यह बन जाता है /bin/bash -o pipefail /tmp/ff- getopt(या समान) पार्सिंग का उपयोग करते हुए optarg, जो कि अगला आइटम है ARGV, तर्क के रूप में। करने के लिए -o, तो यह विफल रहता है। यदि आप एक रैपर बनाने के लिए थे (कहो, bash-pfकि बस किया exec /bin/bash -o pipefail "$@", और उस #!लाइन पर डाल दिया , तो वह काम करेगा। यह भी देखें: en.wikipedia.org/wiki/Shebang_%28Unix%29
मैं क्या जब संभव से बाहर निकलने के कोड को खिलाने के लिए है fooमें bar। उदाहरण के लिए, यदि मुझे पता है कि fooकभी भी केवल अंकों के साथ एक रेखा नहीं बनती है, तो मैं सिर्फ बाहर निकलने के कोड से निपट सकता हूं:
{ foo; echo "$?"; } | awk '!/[^0-9]/ {exit($0)} {…}'
या अगर मुझे पता है कि fooकभी भी आउटपुट में सिर्फ एक लाइन नहीं होती है .:
{ foo; echo .; echo "$?"; } | awk '/^\.$/ {getline; exit($0)} {…}'
यह हमेशा किया जा सकता है, अगर barसभी लाइन पर अंतिम पंक्ति में काम करने का कोई तरीका है , और अंतिम लाइन पर इसके निकास कोड के रूप में पास करें।
यदि barएक जटिल पाइपलाइन है, जिसके आउटपुट की आपको आवश्यकता नहीं है, तो आप एक अलग फ़ाइल डिस्क्रिप्टर पर एक्जिट कोड को प्रिंट करके इसके कुछ भाग को बायपास कर सकते हैं।
exit_codes=$({ { foo; echo foo:"$?" >&3; } |
{ bar >/dev/null; echo bar:"$?" >&3; }
} 3>&1)
इसके बाद $exit_codesआम तौर पर होता है foo:X bar:Y, लेकिन यह हो सकता है bar:Y foo:Xकि barइसके सभी इनपुट को पढ़ने से पहले क्विट किया जाए या यदि आप अशुभ हैं। मुझे लगता है कि 512 बाइट्स तक के पाइप सभी यूनियनों पर परमाणु हैं, इसलिए जब तक टैग स्ट्रिंग्स 507 बाइट्स के नीचे हैं तब तक भागों foo:$?और bar:$?हिस्सों को इंटरमिक्स नहीं किया जाएगा।
यदि आपको आउटपुट को कैप्चर करने की आवश्यकता है bar, तो यह मुश्किल हो जाता है। आप आउटपुट को barएक आउट कोड कोड के समान दिखने वाली लाइन में शामिल नहीं करने के लिए कभी भी आउटपुट की व्यवस्था करके उपरोक्त तकनीकों को जोड़ सकते हैं , लेकिन यह पूरी तरह से हो जाता है।
output=$(echo;
{ { foo; echo foo:"$?" >&3; } |
{ bar | sed 's/^/^/'; echo bar:"$?" >&3; }
} 3>&1)
nl='
'
foo_exit_code=${output#*${nl}foo:}; foo_exit_code=${foo_exit_code%%$nl*}
bar_exit_code=${output#*${nl}bar:}; bar_exit_code=${bar_exit_code%%$nl*}
output=$(printf %s "$output" | sed -n 's/^\^//p')
और, ज़ाहिर है, स्थिति को संग्रहीत करने के लिए एक अस्थायी फ़ाइल का उपयोग करने का सरल विकल्प है । सरल, लेकिन उत्पादन में इतना आसान नहीं :
/tmpएकमात्र ऐसी जगह होती है, जहाँ कोई स्क्रिप्ट फ़ाइलों को लिखने में सक्षम होती है। उपयोग करें mktemp, जो आजकल POSIX नहीं है, लेकिन सभी गंभीर यूनियनों पर उपलब्ध है।foo_ret_file=$(mktemp -t)
{ foo; echo "$?" >"$foo_ret_file"; } | bar
bar_ret=$?
foo_ret=$(cat "$foo_ret_file"; rm -f "$foo_ret_file")
पाइपलाइन से शुरू:
foo | bar | baz
यहां केवल POSIX शेल और कोई अस्थायी फ़ाइलों का उपयोग करके एक सामान्य समाधान है:
exec 4>&1
error_statuses="`((foo || echo "0:$?" >&3) |
(bar || echo "1:$?" >&3) |
(baz || echo "2:$?" >&3)) 3>&1 >&4`"
exec 4>&-
$error_statuses किसी भी विफल प्रक्रियाओं की स्थिति कोड यादृच्छिक क्रम में अनुक्रमित के साथ बताती है कि किस कमांड ने प्रत्येक स्थिति का उत्सर्जन किया है।
# if "bar" failed, output its status:
echo "$error_statuses" | grep '1:' | cut -d: -f2
# test if all commands succeeded:
test -z "$error_statuses"
# test if the last command succeeded:
! echo "$error_statuses" | grep '2:' >/dev/null
$error_statusesमेरे परीक्षणों में चारों ओर उद्धरण नोट करें ; उनके बिना grepअंतर नहीं हो सकता है क्योंकि नए स्थान रिक्त स्थान के लिए मजबूर हो जाते हैं।
इसलिए मैं lesmana की तरह एक जवाब का योगदान करना चाहता था, लेकिन मुझे लगता है कि मेरा शायद थोड़ा सरल और थोड़ा अधिक लाभकारी शुद्ध-बॉर्न-शेल समाधान है:
# You want to pipe command1 through command2:
exec 4>&1
exitstatus=`{ { command1; printf $? 1>&3; } | command2 1>&4; } 3>&1`
# $exitstatus now has command1's exit status.
मुझे लगता है कि यह अंदर से सबसे अच्छी तरह से समझाया गया है - कमांड 1 अपने नियमित आउटपुट को प्रिंटआउट (फाइल डिस्क्रिप्टर 1) पर निष्पादित करेगा और प्रिंट करेगा, फिर एक बार यह पूरा हो जाने पर, प्रिंटफ़ निष्पादित करेगा और कमांडो के निकास कोड को अपने स्टडआउट पर प्रिंट करेगा, लेकिन उस स्टडआउट को पुनर्निर्देशित किया जाएगा। फ़ाइल विवरणक 3।
जबकि कमांड 1 चल रहा है, इसके स्टडआउट को कमांड 2 पर प्रिंट किया जा रहा है (प्रिंटफ का आउटपुट कभी भी कमांड 2 पर नहीं जाता है क्योंकि हम इसे 1 के बजाय फाइल डिस्क्रिप्टर 3 में भेजते हैं, जिसे पाइप पढ़ता है)। फिर हम डिस्क्रिप्टर 4 को फाइल करने के लिए कमांड 2 के आउटपुट को रीडायरेक्ट करते हैं, ताकि वह फाइल डिस्क्रिप्टर 1 से भी बाहर रहे - क्योंकि हम फाइल डिस्क्रिप्टर 1 को थोड़े समय बाद के लिए फ्री चाहते हैं, क्योंकि हम फाइल डिस्क्रिप्टर 3 पर प्रिंटफ आउटपुट को फाइल डिस्क्रिप्टर में वापस लाएँगे। 1 - क्योंकि यही कमांड प्रतिस्थापन (बैकटिक्स) है, कैप्चर करेगा और वही है जो वैरिएबल में रखा जाएगा।
जादू का अंतिम बिट यह है कि पहले exec 4>&1हमने एक अलग कमांड के रूप में किया था - यह बाहरी शेल के स्टॉटआउट की एक प्रति के रूप में फाइल डिस्क्रिप्टर 4 को खोलता है। कमांड प्रतिस्थापन इसके अंदर कमांड के परिप्रेक्ष्य से मानक आउट पर जो कुछ भी लिखा गया है, उस पर कब्जा कर लेगा - लेकिन, चूंकि कमांड प्रतिस्थापन के संबंध में कमांड 2 का आउटपुट डिस्क्रिप्टर 4 फाइल करने जा रहा है, कमांड प्रतिस्थापन इसे कैप्चर नहीं करता है - हालांकि, एक बार जब यह कमांड प्रतिस्थापन का "आउट" हो जाता है, तो यह प्रभावी रूप से स्क्रिप्ट की समग्र फ़ाइल डिस्क्रिप्टर 1 पर जा रहा है।
( exec 4>&1एक अलग कमांड होना चाहिए क्योंकि कमांड प्रतिस्थापन के अंदर फ़ाइल डिस्क्रिप्टर पर लिखने की कोशिश करने पर कई सामान्य गोले इसे पसंद नहीं करते हैं, जो कि "बाहरी" कमांड में खोला जाता है जो प्रतिस्थापन का उपयोग कर रहा है। यह करने के लिए सबसे आसान पोर्टेबल तरीका है।)
आप इसे कम तकनीकी और अधिक चंचल तरीके से देख सकते हैं, जैसे कि कमांड के आउटपुट एक-दूसरे को लीपफ्रॉग कर रहे हैं: कमांड 1 से कमांड 2 पाइप, फिर प्रिंटफ का आउटपुट कमांड 2 पर कूदता है ताकि कमांड 2 इसे पकड़ न सके, और फिर कमांड 2 का आउटपुट कमांड प्रतिस्थापन के ऊपर और बाहर प्रिंटफ लैंड के रूप में समय से पहले ही स्थानापन्न द्वारा कब्जा कर लिया जाता है ताकि यह चर में समाप्त हो जाए, और कमांड 2 का उत्पादन मानक तरीके से लिखे जाने के रूप में मानक तरीके से लिखा जा रहा है। एक सामान्य पाइप में।
इसके अलावा, जैसा कि मैं समझता हूं, $?अभी भी पाइप में दूसरी कमांड का रिटर्न कोड होगा, क्योंकि चर असाइनमेंट, कमांड सबस्टेशन और कम्पाउंड कमांड सभी प्रभावी रूप से उनके अंदर कमांड के रिटर्न कोड के लिए पारदर्शी हैं, इसलिए रिटर्न की स्थिति कमांड 2 को प्रचारित किया जाना चाहिए - यह, और एक अतिरिक्त फ़ंक्शन को परिभाषित करने के लिए नहीं है, यही कारण है कि मुझे लगता है कि यह lesmana द्वारा प्रस्तावित एक की तुलना में कुछ बेहतर समाधान हो सकता है।
कैविट्स लेमनमा का उल्लेख करते हुए, यह संभव है कि कमांड 1 फ़ाइल डिस्क्रिप्टर 3 या 4 का उपयोग करके किसी बिंदु पर समाप्त होगा, इसलिए अधिक मजबूत होने के लिए, आप करेंगे:
exec 4>&1
exitstatus=`{ { command1 3>&-; printf $? 1>&3; } 4>&- | command2 1>&4; } 3>&1`
exec 4>&-
ध्यान दें कि मैं अपने उदाहरण में यौगिक आदेशों का उपयोग करता हूं, लेकिन उपधाराएं ( काम के ( )बजाय { }भी काम करेंगी, हालांकि शायद कम कुशल हो सकती हैं।)
फ़ाइल डिस्क्रिप्टर को इनहेरिट करने वाली प्रक्रिया से इनहेरिट करता है, इसलिए संपूर्ण दूसरी पंक्ति फ़ाइल डिस्क्रिप्टर चार को इनहेरिट करेगी, और उसके बाद कंपाउंड कमांड 3>&1फ़ाइल डिस्क्रिप्टर तीन को इनहेरिट करेगी। तो यह 4>&-सुनिश्चित करता है कि आंतरिक कंपाउंड कमांड फाइल डिस्क्रिप्टर चार 3>&-को इनहेरिट नहीं करेगा , और वॉयल फाइल डिस्क्रिप्टर तीन को इनहेरिट नहीं करेगा, इसलिए कमांड 1 को एक 'क्लीनर', अधिक मानक वातावरण मिलता है। आप आंतरिक को भी 4>&-बगल में ले जा सकते हैं 3>&-, लेकिन मैं यह बताता हूं कि क्यों न इसके दायरे को यथासंभव सीमित किया जाए।
मुझे यकीन नहीं है कि चीजें कितनी बार फ़ाइल डिस्क्रिप्टर तीन और चार का सीधे उपयोग करती हैं - मुझे लगता है कि अधिकांश समय प्रोग्राम ऐसे सिस्कल्स का उपयोग करते हैं जो नॉट-ऑन-ऑफ-द-क्षण फ़ाइल डिस्क्रिप्टर लौटाते हैं, लेकिन कभी-कभी कोड डिस्क्रिप्टर 3 को सीधे फाइल करने के लिए लिखते हैं, मैं लगता है (मैं एक प्रोग्राम को देखने के लिए फ़ाइल डिस्क्रिप्टर की जाँच कर सकता हूं कि क्या यह खुला है, और इसका उपयोग कर रहा है या नहीं तो इसके अनुसार अलग-अलग व्यवहार कर रहा है)। इसलिए उत्तरार्द्ध संभवतः सामान्य प्रयोजन के मामलों को ध्यान में रखने और उपयोग करने के लिए सबसे अच्छा है।
-bash: 3: Bad file descriptor।
यदि आपके पास अधिक पैकेज स्थापित हैं, तो आप मिसपीस उपयोगिता का उपयोग कर सकते हैं, जो आपने पूछा था।
ऊपर दिए गए नेमन उपप्रकारों को शुरू करने के { .. }बजाय उपमा के बिना भी ऊपर दिए गए घाव का समाधान किया जा सकता है (यह याद रखना कि समूहीकृत कमांड के इस रूप को हमेशा अर्धविराम के साथ समाप्त करना होता है)। कुछ इस तरह:
{ { { { someprog; echo $? >&3; } | filter >&4; } 3>&1; } | stdintoexitstatus; } 4>&1
मैंने इस निर्माण की जाँच डैश संस्करण 0.5.5 और बैश संस्करणों 3.2.25 और 4.2.42 के साथ की है, इसलिए भले ही कुछ गोले { .. }समूहीकरण का समर्थन न करें , लेकिन यह अभी भी पोसिक्स अनुरूप है।
set -o pipefailksh या किसी भी संख्या में स्प्रिंकल waitकमांड के साथ भी। मुझे लगता है कि यह कम से कम, भाग में, ksh के लिए एक पार्सिंग मुद्दा हो सकता है, जैसे कि अगर मैं उपधाराओं का उपयोग करने के लिए चिपक जाता हूं तो यह ठीक काम करता है, लेकिन यहां तक कि ifksh के लिए उप संस्करण को चुनने के लिए लेकिन दूसरों के लिए यौगिक आदेशों को छोड़ दें, यह विफल रहता है ।
यह पोर्टेबल है, यानी किसी भी POSIX अनुरूप शेल के साथ काम करता है, वर्तमान निर्देशिका को लिखने योग्य होने की आवश्यकता नहीं है और एक ही चाल को एक साथ चलाने के लिए कई लिपियों का उपयोग करने की अनुमति देता है।
(foo;echo $?>/tmp/_$$)|(bar;exit $(cat /tmp/_$$;rm /tmp/_$$))
संपादित करें: यहां गिल्स की टिप्पणियों के बाद एक मजबूत संस्करण है:
(s=/tmp/.$$_$RANDOM;((foo;echo $?>$s)|(bar)); exit $(cat $s;rm $s))
Edit2: और यहां कुछ हल्का संस्करण है, जो डबियसिम टिप्पणी के बाद है:
(s=/tmp/.$$_$RANDOM;{foo;echo $?>$s;}|bar; exit $(cat $s;rm $s))
(s=/tmp/.$$_$RANDOM;{foo;echo $?>$s;}|bar; exit $(cat $s;rm $s)):। @ जोहान: मैं मानता हूं कि यह बैश के साथ आसान है, लेकिन कुछ संदर्भों में, यह जानने के लिए कि बैश से कैसे बचा जाए।
यदि आप सामान्य समाधानों में से एक का उपयोग करने में सक्षम नहीं हैं, तो @Patrik के उत्तर के लिए एक ऐडऑन के रूप में निम्नलिखित है।
यह उत्तर मानता है:
$PIPESTATUSऔर न हीset -o pipefailअतिरिक्त मान्यताओं। आप सभी से छुटकारा पा सकते हैं, लेकिन यह नुस्खा बहुत ज्यादा पसंद करता है, इसलिए यह यहां नहीं है:
- आप सभी यह जानना चाहते हैं कि PIPE में सभी कमांड्स का कोड 0 है।
- आपको अतिरिक्त साइड बैंड जानकारी की आवश्यकता नहीं है।
- आपका शेल सभी पाइप कमांड के लौटने का इंतजार करता है।
इससे पहले: foo | bar | bazहालाँकि, यह केवल अंतिम कमांड का निकास कोड देता है ( baz)
वांटेड: (सत्य) $?नहीं होना चाहिए 0, यदि पाइप में कोई भी कमांड विफल हो गया है
उपरांत:
TMPRESULTS="`mktemp`"
{
rm -f "$TMPRESULTS"
{ foo || echo $? >&9; } |
{ bar || echo $? >&9; } |
{ baz || echo $? >&9; }
#wait
! read TMPRESULTS <&8
} 9>>"$TMPRESULTS" 8<"$TMPRESULTS"
# $? now is 0 only if all commands had exit code 0
व्याख्या की:
mktemp। यह आमतौर पर तुरंत एक फ़ाइल बनाता है/tmpwaitलिए आवश्यक है ksh, क्योंकि kshअन्य सभी पाइप कमांड के खत्म होने का इंतजार नहीं करते हैं। हालाँकि कृपया ध्यान दें कि यदि कुछ पृष्ठभूमि कार्य मौजूद हैं, तो अवांछित साइडइफेक्ट्स हैं, इसलिए मैंने इसे डिफ़ॉल्ट रूप से टिप्पणी की। यदि प्रतीक्षा में दर्द नहीं होता है, तो आप इसे टिप्पणी कर सकते हैं।readरिटर्न देता है false, तो trueएक त्रुटि इंगित करता हैयह एक एकल आदेश के लिए एक प्लगइन प्रतिस्थापन के रूप में इस्तेमाल किया जा सकता है और केवल निम्नलिखित की आवश्यकता है:
/proc/fd/Nकीड़े:
इस स्क्रिप्ट में एक बग /tmpहै जो अंतरिक्ष से बाहर चलता है। आप इस कृत्रिम मामले के खिलाफ संरक्षण की जरूरत है, भी, आप इसे इस रूप में कर सकते हैं, लेकिन इस नुकसान है, कि की संख्या 0में 000पाइप में आदेशों की संख्या पर निर्भर करता है, इसलिए यह थोड़ा और अधिक जटिल है:
TMPRESULTS="`mktemp`"
{
rm -f "$TMPRESULTS"
{ foo; printf "%1s" "$?" >&9; } |
{ bar; printf "%1s" "$?" >&9; } |
{ baz; printf "%1s" "$?" >&9; }
#wait
read TMPRESULTS <&8
[ 000 = "$TMPRESULTS" ]
} 9>>"$TMPRESULTS" 8<"$TMPRESULTS"
पोर्टबलिटी नोट:
kshऔर इसी तरह के गोले जो केवल अंतिम पाइप कमांड के लिए प्रतीक्षा करते हैं, को waitअनियोजित करने की आवश्यकता होती है
अंतिम उदाहरण printf "%1s" "$?"इसके बजाय उपयोग करता है echo -n "$?"क्योंकि यह अधिक पोर्टेबल है। हर प्लेटफ़ॉर्म -nसही ढंग से व्याख्या नहीं करता है।
printf "$?"printf "%1s"यदि आप स्क्रिप्ट को किसी सचमुच टूटे हुए प्लेटफ़ॉर्म पर चलाते हैं , तो यह भी करेगा । (पढ़ें: यदि आप में कार्यक्रम करने के लिए होता है paranoia_mode=extreme।)
एफडी 8 और एफडी 9 उन प्लेटफार्मों पर अधिक हो सकते हैं जो कई अंकों का समर्थन करते हैं। AFAIR एक POSIX अनुरूप शेल को केवल एकल अंकों का समर्थन करने की आवश्यकता होती है।
डेबियन 8.2 के साथ परीक्षण किया गया था sh, bash, ksh, ash, sashऔर यहां तक किcsh
थोड़ी सावधानी के साथ, यह काम करना चाहिए:
foo-status=$(mktemp -t)
(foo; echo $? >$foo-status) | bar
foo_status=$(cat $foo-status)
निम्न 'यदि' ब्लॉक तभी चलेगा जब 'कमांड' सफल हुआ:
if command; then
# ...
fi
विशेष रूप से बोलते हुए, आप कुछ इस तरह से चला सकते हैं:
haconf_out=/path/to/some/temporary/file
if haconf -makerw > "$haconf_out" 2>&1; then
grep -iq "Cluster already writable" "$haconf_out"
# ...
fi
जो haconf -makerwअपने स्टडआउट और stderr को "$ haconf_out" पर चलाएगा और स्टोर करेगा । यदि से लौटाया गया मान haconfसत्य है, तो 'if' ब्लॉक निष्पादित grepहोगा और "$ haconf_out" को पढ़ेगा, इसे "क्लस्टर पहले से ही लिखने योग्य" के विरुद्ध मिलान करने की कोशिश करेगा।
ध्यान दें कि पाइप स्वचालित रूप से खुद को साफ करते हैं; पुनर्निर्देशन के साथ आपको "$ haconf_out" को हटाने के लिए सावधानी बरतनी होगी।
के रूप में के रूप में सुरुचिपूर्ण नहीं है pipefail, लेकिन एक वैध विकल्प अगर यह कार्यक्षमता पहुंच के भीतर नहीं है।
Alternate example for @lesmana solution, possibly simplified.
Provides logging to file if desired.
=====
$ cat z.sh
TEE="cat"
#TEE="tee z.log"
#TEE="tee -a z.log"
exec 8>&- 9>&-
{
{
{
{ #BEGIN - add code below this line and before #END
./zz.sh
echo ${?} 1>&8 # use exactly 1x prior to #END
#END
} 2>&1 | ${TEE} 1>&9
} 8>&1
} | exit $(read; printf "${REPLY}")
} 9>&1
exit ${?}
$ cat zz.sh
echo "my script code..."
exit 42
$ ./z.sh; echo "status=${?}"
my script code...
status=42
$
(कम से कम बैश के साथ) set -eएक के साथ संयुक्त एक पाइपलाइफ का स्पष्ट रूप से अनुकरण करने और पाइप की त्रुटि पर बाहर निकलने के लिए उपधारा का उपयोग कर सकता है
set -e
foo | bar
( exit ${PIPESTATUS[0]} )
rest of program
तो अगर fooकिसी कारण से विफल हो जाता है - बाकी कार्यक्रम निष्पादित नहीं किया जाएगा और स्क्रिप्ट इसी त्रुटि कोड के साथ बाहर निकलता है। (यह मानता है कि fooअपनी त्रुटि को छापता है, जो विफलता के कारण को समझने के लिए पर्याप्त है)
संपादित करें : यह उत्तर गलत है, लेकिन दिलचस्प है, इसलिए मैं इसे भविष्य के संदर्भ के लिए छोड़ दूंगा।
!कमांड में
एक जोड़ने पर रिटर्न कोड को निष्क्रिय कर देता है।
http://tldp.org/LDP/abs/html/exit-status.html
# =========================================================== #
# Preceding a _pipe_ with ! inverts the exit status returned.
ls | bogus_command # bash: bogus_command: command not found
echo $? # 127
! ls | bogus_command # bash: bogus_command: command not found
echo $? # 0
# Note that the ! does not change the execution of the pipe.
# Only the exit status changes.
# =========================================================== #
ls, के निकास कोड नहीं उलटनेbogus_command