प्रक्रिया प्रतिस्थापन का उपयोग करते समय मैं निकास कोड / त्रुटियों को सही तरीके से कैसे पकड़ता हूं?


13

मेरे पास एक स्क्रिप्ट है, जो एसओ पर एक प्रश्नोत्तर से ली गई विधि का उपयोग करके एक सरणी में नाम दर्ज करती है :

unset ARGS
ARGID="1"
while IFS= read -r -d $'\0' FILE; do
    ARGS[ARGID++]="$FILE"
done < <(find "$@" -type f -name '*.txt' -print0)

यह बढ़िया काम करता है और सभी प्रकार के फ़ाइलनाम भिन्नरूपों को पूरी तरह से संभालता है। कभी-कभी, हालांकि, मैं स्क्रिप्ट के लिए एक गैर-मौजूदा फ़ाइल पास करूंगा, जैसे:

$ findscript.sh existingfolder nonexistingfolder
find: `nonexistingfile': No such file or directory
...

सामान्य परिस्थितियों में मेरे पास स्क्रिप्ट को एग्जिट कोड को कुछ इस तरह से कैप्चर करना होगा RET=$?और आगे बढ़ने के तरीके का उपयोग करना होगा। यह ऊपर की प्रक्रिया प्रतिस्थापन के साथ काम नहीं करता है।

इस तरह के मामलों में सही प्रक्रिया क्या है? मैं रिटर्न कोड कैसे पकड़ सकता हूं? क्या निर्धारित प्रक्रिया में कुछ गलत हुआ है, यह निर्धारित करने के लिए अन्य उपयुक्त तरीके हैं?

जवाबों:


5

आप बहुत ही आसानी से किसी भी सब्सक्राइब्ड प्रक्रिया से रिटर्न प्राप्त कर सकते हैं। यह प्रक्रिया प्रतिस्थापन का सच है:

while IFS= read -r -d $'\0' FILE || 
    ! return=$FILE
do    ARGS[ARGID++]="$FILE"
done < <(find . -type f -print0; printf "$?")

अगर मैं उसे चलाता हूं तो बहुत अंतिम लाइन - (या \0सीमांकित खंड जैसा कि मामला हो सकता है) की findस्थिति लौटने वाली है। readएक EOF होने पर 1 को वापस करने जा रहा है - इसलिए केवल पढ़ने के लिए बहुत अंतिम जानकारी के लिए एकमात्र समय $returnनिर्धारित $FILEहै।

मैं printfएक अतिरिक्त \newline जोड़ने से रखने के लिए उपयोग करता हूं - यह महत्वपूर्ण है क्योंकि यहां तक ​​कि readनियमित रूप से प्रदर्शन किया जाता है - एक जिसमें आप \0NULs पर परिसीमन नहीं करते हैं - उन मामलों में 0 के अलावा वापस आने वाला है जब इसे अभी-अभी पढ़ा गया डेटा समाप्त नहीं होता है एक \nईवलाइन। इसलिए यदि आपकी अंतिम पंक्ति ई-लाईन के साथ समाप्त नहीं \nहोती है, तो चर में आपके रीड में अंतिम मान आपकी वापसी होने वाला है।

ऊपर कमांड चलाना और फिर:

echo "$return"

आउटपुट

0

और अगर मैं इस प्रक्रिया प्रतिस्थापन भाग को बदल ...

...
done < <(! find . -type f -print0; printf "$?")
echo "$return"

आउटपुट

1

एक अधिक सरल प्रदर्शन:

printf \\n%s list of lines printed to pipe |
while read v || ! echo "$v"
do :; done

आउटपुट

pipe

और वास्तव में, जब तक आप चाहते हैं, तब तक अंतिम चीज है जिसे आप प्रक्रिया प्रतिस्थापन के भीतर से रोकते हुए लिखते हैं - या कोई भी उप-प्रक्रिया जिसमें से आप इस तरह से पढ़ते हैं - तब $FILEहमेशा वह वापसी स्थिति होने जा रही है जिसे आप चाहते हैं के माध्यम से है। और इसलिए || ! return=...हिस्सा कड़ाई से आवश्यक नहीं है - इसका उपयोग केवल अवधारणा को प्रदर्शित करने के लिए किया जाता है।


5

प्रक्रिया प्रतिस्थापन में प्रक्रियाएं अतुल्यकालिक हैं: शेल उन्हें लॉन्च करता है और फिर मरने पर पता लगाने का कोई तरीका नहीं देता है। इसलिए आप बाहर निकलने की स्थिति प्राप्त नहीं कर पाएंगे।

आप किसी फ़ाइल से बाहर निकलने की स्थिति लिख सकते हैं, लेकिन यह सामान्य रूप से अनाड़ी है क्योंकि आप नहीं जान सकते कि फ़ाइल कब लिखी गई है। यहाँ, फ़ाइल लूप के अंत के तुरंत बाद लिखी गई है, इसलिए इसके लिए प्रतीक्षा करना उचित है।

 < <(find …; echo $? >find.status.tmp; mv find.status.tmp find.status)
while ! [ -e find.status ]; do sleep 1; done
find_status=$(cat find.status; rm find.status)

एक अन्य दृष्टिकोण एक नामित पाइप और एक पृष्ठभूमि प्रक्रिया का उपयोग करना है (जो आप कर सकते waitहैं)।

mkfifo find_pipe
find  >find_pipe &
find_pid=$!
 <find_pipe
wait $find_pid
find_status=$?

यदि न तो दृष्टिकोण उपयुक्त है, तो मुझे लगता है कि आपको अधिक सक्षम भाषा के लिए सिर की आवश्यकता होगी, जैसे कि पर्ल, पायथन या रूबी।


इस उत्तर के लिए धन्यवाद। आपके द्वारा बताए गए तरीके ठीक काम करते हैं, लेकिन मुझे यह स्वीकार करना चाहिए कि वे अनुमान से थोड़ा अधिक जटिल हैं। मेरे मामले में मैं एक लूप के लिए बस गया था, जिसमें एक प्रश्न में दिखाया गया था कि सभी तर्कों के माध्यम से पुनरावृत्ति होती है और उनमें से एक फ़ाइल या फ़ोल्डर नहीं होने पर एक त्रुटि प्रिंट करता है। हालांकि यह अन्य प्रकार की त्रुटियों को नहीं संभालता है जो कि प्रतिस्थापित प्रक्रिया में हो सकती है यह इस विशिष्ट मामले के लिए पर्याप्त है। अगर मुझे कभी इस तरह की स्थितियों में अधिक परिष्कृत त्रुटि से निपटने की विधि की आवश्यकता होती है, तो मैं निश्चित रूप से आपके उत्तर पर वापस आऊंगा।
Glutanimate

2

एक कोप्रोसेस का उपयोग करें । coprocबिलिन का उपयोग करके आप एक उपप्रकार शुरू कर सकते हैं, इसके आउटपुट को पढ़ सकते हैं और इसकी निकास स्थिति की जांच कर सकते हैं:

coproc LS { ls existingdir; }
LS_PID_=$LS_PID
while IFS= read i; do echo "$i"; done <&"$LS"
wait "$LS_PID_"; echo $?

यदि निर्देशिका मौजूद नहीं है, waitतो गैर-शून्य स्थिति कोड से बाहर निकल जाएगा।

वर्तमान में PID को किसी अन्य चर में कॉपी करना आवश्यक है क्योंकि कॉल करने $LS_PIDसे पहले परेशान हो जाएगा wait। देखें बैश unsets _PID चर इससे पहले कि मैं coproc पर इंतजार कर सकते हैं * जानकारी के लिए।


1
मैं उत्सुक हूं कि जब कोई व्यक्ति <-"$ LS" बनाम रीड -u $ LS का उपयोग करेगा? - धन्यवाद
ब्रायन क्रिसमैन

1
@BrianChrisman इस उदाहरण में, शायद कभी नहीं। read -uबस के रूप में अच्छी तरह से काम करना चाहिए। उदाहरण का मतलब सामान्य होना था और यह दिखाना था कि कैसे कोप्रोसेस के आउटपुट को किसी अन्य कमांड में पाइप किया जा सकता है।
फेउर्मुरमेल

1

एक दृष्टिकोण है:

status=0
token="WzNZY3CjqF3qkasn"    # some random string
while read line; do
    if [[ "$line" =~ $token:([[:digit:]]+) ]]; then
        status="${BASH_REMATCH[1]}"
    else
        echo "$line"
    fi
done < <(command; echo "$token:$?")
echo "Return code: $status"

यह विचार कमांड के पूरा होने के बाद यादृच्छिक टोकन के साथ निकास स्थिति को प्रतिध्वनित करने के लिए है, और फिर बाहर निकलने की स्थिति को देखने और निकालने के लिए बैश नियमित अभिव्यक्तियों का उपयोग करें। टोकन का उपयोग आउटपुट में देखने के लिए एक अद्वितीय स्ट्रिंग बनाने के लिए किया जाता है।

यह शायद एक सामान्य प्रोग्रामिंग अर्थ में ऐसा करने का सबसे अच्छा तरीका नहीं है, लेकिन इसे बैश में संभालना कम से कम दर्दनाक तरीका हो सकता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.