मेरी दो प्रक्रियाएँ हैं 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
(उद्धरण की कमी पर ध्यान दें!)।
pipestatus
zsh में। दुर्भाग्य से अन्य गोले में यह सुविधा नहीं है।
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 pipefail
POSIX में नहीं है।
#!/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 pipefail
ksh या किसी भी संख्या में स्प्रिंकल wait
कमांड के साथ भी। मुझे लगता है कि यह कम से कम, भाग में, ksh के लिए एक पार्सिंग मुद्दा हो सकता है, जैसे कि अगर मैं उपधाराओं का उपयोग करने के लिए चिपक जाता हूं तो यह ठीक काम करता है, लेकिन यहां तक कि if
ksh के लिए उप संस्करण को चुनने के लिए लेकिन दूसरों के लिए यौगिक आदेशों को छोड़ दें, यह विफल रहता है ।
यह पोर्टेबल है, यानी किसी भी 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
। यह आमतौर पर तुरंत एक फ़ाइल बनाता है/tmp
wait
लिए आवश्यक है 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