क्या यह बग में एक बग है? `वापसी` एक पाइप से बुलाया अगर समारोह छोड़ नहीं है


16

मैं कुछ अजीब समस्याओं के साथ हाल ही में कर रहा हूँ। अपनी स्क्रिप्ट को सरल बनाने की कोशिश करते हुए, मैं इस छोटे से कोड के साथ आया:

$ o(){ echo | while read -r; do return 0; done; echo $?;}; o
0
$ o(){ echo | while read -r; do return 1; done; echo $?;}; o
1

returnमुद्रण के बिना समारोह से बाहर निकलना चाहिए था $?, है न? ठीक है, तो मैंने जाँच की कि क्या मैं अकेले पाइप से लौट सकता हूँ:

$ echo | while read -r; do return 1; done
bash: return: can only `return' from a function or sourced script

whileलूप के बिना भी ऐसा ही होता है :

$ foo(){ : | return 1; echo "This should not be printed.";}
$ foo
This should not be printed.

क्या मुझे यहाँ कुछ याद आ रहा है? एक Google खोज इस बारे में कुछ नहीं लाया! मेरा बैश संस्करण ४.२.३ Whe (१) है-डेबियन व्हीज़ी पर।


मेरे जवाब में सुझाई गई सेटिंग्स में कुछ भी गलत है जो आपकी स्क्रिप्ट को उस सहज तरीके का व्यवहार करने की अनुमति देता है जिसे आपने करने की उम्मीद की थी?
jlliagre

@jlliagre यह हजारों लाइनों पर एक बल्कि जटिल स्क्रिप्ट है। कुछ और तोड़ने की चिंता के साथ, मैं फ़ंक्शन के भीतर एक पाइप चलाने से बचना पसंद करता हूं, इसलिए मैंने इसे एक प्रक्रिया प्रतिस्थापन के साथ बदल दिया। धन्यवाद!
टेरेसा ई जूनियर

पहले दो उदाहरणों को क्यों नहीं हटाया गया, अगर whileप्रजनन की आवश्यकता नहीं है? यह बिंदु से विचलित करता है।
मोनिका

@LightnessRacesinOrbit एक whileपाश एक पाइप के साथ एक बहुत ही सामान्य उपयोग है return। दूसरा उदाहरण इस बिंदु पर अधिक सीधे है, लेकिन यह ऐसा कुछ है जो मुझे विश्वास नहीं है कि कोई भी कभी भी उपयोग करेगा ...
टेरेसा ई जूनियर

1
दुर्भाग्य से मेरा सही उत्तर हटा दिया गया है ... आप एक ग्रे ज़ोन में हैं क्योंकि आप कुछ ऐसा करते हैं जो अनिर्दिष्ट हो। व्यवहार इस बात पर निर्भर करता है कि शेल पाइपों की व्याख्या कैसे करता है और यह बॉर्न शेल और कोर्न शेल के बीच भी अलग है, जबकि ksh sh स्रोतों से प्राप्त हुआ था। बॉर्न शेल में, जबकि लूप एक उपधारा में होता है इसलिए आप इको को बैश के साथ देखते हैं, ksh में जबकि लूप अग्रभूमि प्रक्रिया है और इस प्रकार ksh आपके उदाहरण के साथ इको नहीं कहता है।
शिल्पी

जवाबों:


10

संबंधित: /programming//a/7804208/4937930

यह एक बग नहीं है कि आप एक स्क्रिप्ट से बाहर नहीं जा सकते हैं या एक फ़ंक्शन से exitया returnउपधाराओं में वापस आ सकते हैं । वे एक अन्य प्रक्रिया में निष्पादित होते हैं और मुख्य प्रक्रिया को प्रभावित नहीं करते हैं।

इसके अलावा, मुझे लगता है कि आप (शायद) अपरिभाषित कल्पना पर मार के अनिर्धारित व्यवहार देख रहे हैं। किसी फ़ंक्शन में, returnउप-कमांड आदेशों के शीर्ष स्तर के लिए कोई त्रुटि नहीं होती है और यह केवल व्यवहार करता है exit

IMHO यह एक बग बग है, जो returnइस बात पर निर्भर करता है कि मुख्य कथन किसी फ़ंक्शन में है या नहीं।

#!/bin/bash

o() {
    # Runtime error, but no errors are asserted,
    # each $? is set to the return code.
    echo | return 10
    echo $?
    (return 11)
    echo $?

    # Valid, each $? is set to the exit code.
    echo | exit 12
    echo $?
    (exit 13)
    echo $?
}
o

# Runtime errors are asserted, each $? is set to 1.
echo | return 20
echo $?
(return 21)
echo $?

# Valid, each $? is set to the exit code.
echo | exit 22
echo $?
(exit 23)
echo $?

आउटपुट:

$ bash script.sh 
10
11
12
13
script.sh: line 20: return: can only `return' from a function or sourced script
1
script.sh: line 22: return: can only `return' from a function or sourced script
1
22
23

त्रुटि वर्बोसिटी की कमी अनिर्दिष्ट हो सकती है। लेकिन तथ्य यह है कि returnएक उप-स्तरीय कमांड अनुक्रम से एक सबशेल में काम नहीं करता है, और विशेष रूप से सब्सक्रिप्शन से बाहर नहीं निकलता है, मौजूदा दस्तावेजों ने मुझे पहले से ही उम्मीद की है। ओपी उपयोग कर सकते हैं exit 1 || return 1जहां वे उपयोग करने की कोशिश कर रहे हैं return, और फिर अपेक्षित व्यवहार प्राप्त करना चाहिए। संपादित करें: @ हर्बर्ट का उत्तर इंगित करता है कि उप-प्रकार returnमें टॉपवेल कार्य कर रहा है exit(लेकिन केवल उप-संस्करण से)।
संदिग्ध

1
@dubiousjim मेरी स्क्रिप्ट अपडेट की गई। मेरा मतलब है returnकि एक साधारण उप-अनुक्रम अनुक्रम को किसी भी मामलों में एक रनटाइम त्रुटि के रूप में माना जाना चाहिए , लेकिन वास्तव में यह तब नहीं होता है जब यह एक fucntion में होता है। इस समस्या पर gnu.bash.bug में भी चर्चा की गई है , लेकिन कोई निष्कर्ष नहीं निकला है।
ययगाशी

1
आपका उत्तर सही नहीं है क्योंकि यह अनिर्दिष्ट है कि क्या लूप सबस्क्रिप्शन में है या अग्रभूमि प्रक्रिया है। वास्तविक शेल के कार्यान्वयन के बावजूद, returnकथन एक फ़ंक्शन में है और इस प्रकार कानूनी है। परिणामी व्यवहार हालांकि अनिर्दिष्ट है।
10

आपको यह नहीं लिखना चाहिए कि यह एक अनिर्दिष्ट व्यवहार है जबकि तथ्य उप-घटक एक उपधारा में हैं जो बैश मैनुअल पेज में दर्ज़ किए गए हैं। आपको ऐसा व्यवहार नहीं लिखना चाहिए जो संभवतया अपरिभाषित चश्मे पर आधारित हो जबकि POSIX अनुमत व्यवहारों को निर्दिष्ट करता हो। आपको बैश बग पर संदेह नहीं होना चाहिए, जबकि बैश एक फ़ंक्शन में वापसी की अनुमति देकर पॉसिक्स मानक का पालन कर रहा है, लेकिन बाहर नहीं।
jlliagre

17

यह एक बग नहीं है, bashलेकिन इसके प्रलेखित व्यवहार में है :

एक पाइपलाइन में प्रत्येक कमांड को अपने स्वयं के उप-भाग में निष्पादित किया जाता है

returnअनुदेश एक समारोह परिभाषा के अंदर मान्य किया जा रहा है, लेकिन एक subshell में होने के रूप में अच्छी तरह से, यह बहुत अगले निर्देश अपनी मूल खोल प्रभावित नहीं करता, है echo, भले ही मार डाला जाता है। यह फिर भी एक गैर पोर्टेबल शेल निर्माण है क्योंकि POSIX मानक कमांड को एक सब-शेल (डिफ़ॉल्ट) या शीर्ष एक (एक अनुमत एक्सटेंशन) में निष्पादित करने के लिए पाइपलाइन की रचना करने की अनुमति देता है।

इसके अतिरिक्त, बहु-कमान पाइपलाइन की प्रत्येक कमांड एक उप-वातावरण में है; एक विस्तार के रूप में, हालांकि, एक पाइपलाइन में किसी भी या सभी आदेशों को मौजूदा वातावरण में निष्पादित किया जा सकता है। अन्य सभी आदेशों को वर्तमान शेल वातावरण में निष्पादित किया जाएगा।

उम्मीद है, आप bashकुछ विकल्पों के साथ अपनी अपेक्षा के अनुरूप व्यवहार करना बता सकते हैं :

$ set +m # disable job control
$ shopt -s lastpipe # do not run the last command of a pipeline a subshell 
$ o(){ echo | while read -r; do return 0; done; echo $?;}
$ o
$          <- nothing is printed here

1
चूंकि returnफ़ंक्शन को नहीं छोड़ा जाएगा, इसलिए यह अधिक समझ में नहीं आएगा यदि शेल केवल मुद्रित हो bash: return: can only `return' from a function or sourced script, बजाय उपयोगकर्ता को यह गलत अर्थ दिए कि फ़ंक्शन वापस आ सकता है?
टेरेसा ई जूनियर

2
मैं कहीं भी दस्तावेज में यह नहीं देखता कि सदस्यता के अंदर वापसी वैध है। मुझे विश्वास है कि इस फीचर को ksh से कॉपी किया गया था, फंक्शन के बाहर का रिटर्न स्टेटमेंट या सोर्स की गई स्क्रिप्ट एक्जिट की तरह व्यवहार करती है । मैं मूल बॉर्न शेल के बारे में निश्चित नहीं हूं।
congonglm

1
@ जैलीग्रे: हो सकता है कि टेरेसा इस शब्दावली के बारे में उलझन में हैं कि वह क्या मांग रही हैं, लेकिन मैं नहीं देखती कि यदि आप returnएक उपधारा से निष्पादित करते हैं तो डायग्नोस्टिक जारी करने के लिए बैश के लिए "मुश्किल" क्यों होगा । सब के बाद, यह जानता है कि यह एक उपधारा में है, जैसा कि $BASH_SUBSHELLचर का सबूत है । सबसे बड़ी समस्या यह है कि इससे झूठी सकारात्मकता पैदा हो सकती है; एक उपयोगकर्ता जो समझता है कि उपश्रेणी कैसे काम करती है, वह स्क्रिप्ट लिख सकता है जो एक उपधारा को समाप्त करने के returnबदले में उपयोग करता है exit। (और, निश्चित रूप से, ऐसे वैध मामले हैं जहाँ कोई व्यक्ति चर सेट करना चाहता है या cdएक उपधारा में कर सकता है।)
स्कॉट

1
@ मुझे लगता है कि मैं स्थिति को अच्छी तरह समझता हूं। एक पाइप एक सबशेल बनाता है, और returnएक वास्तविक फ़ंक्शन के अंदर होने के बाद, असफल होने के बजाय सबस्क्रिप्शन से वापस आ रहा है। समस्या यह है कि help returnविशेष रूप से कहा गया है: Causes a function or sourced script to exit with the return value specified by N.प्रलेखन को पढ़ने से, कोई भी उपयोगकर्ता यह अपेक्षा करेगा कि वह कम से कम विफल हो या चेतावनी प्रिंट न करे, लेकिन कभी भी ऐसा व्यवहार न करें exit
टेरेसा ई जूनियर

1
यह मुझे लगता है कि जो कोई भी एक फ़ंक्शन return में एक सब-सब्सक्रिप्शन में फ़ंक्शन से लौटने की उम्मीद करता है (मुख्य शेल प्रक्रिया में) सब्स्क्रिप्शन को बहुत अच्छी तरह से नहीं समझता है। इसके विपरीत, मैं एक पाठक से अपेक्षा करूंगा कि जो उपसमूह को समझने के लिए किसी फ़ंक्शन return में एक उप-प्रकार को समाप्त करने की अपेक्षा करता है, ठीक उसी तरह exit
स्कॉट

6

POSIX दस्तावेज़ के अनुसार, फ़ंक्शन या स्रोत स्क्रिप्ट के बाहर का उपयोग returnअनिर्दिष्ट है । तो, यह आपके शेल को संभालने के लिए निर्भर करता है।

SystemV खोल त्रुटि, रिपोर्ट में जबकि जाएगा ksh, returnया जैसे समारोह के बाहर sourced स्क्रिप्ट व्यवहार exit। अधिकांश अन्य POSIX गोले और विद्वानों की ओश भी ऐसा ही व्यवहार करते हैं:

$ for s in /bin/*sh /opt/schily/bin/osh; do
  printf '<%s>\n' $s
  $s -c '
    o(){ echo | while read l; do return 0; done; echo $?;}; o
  '
done
</bin/bash>
0
</bin/dash>
0
</bin/ksh>
</bin/lksh>
0
</bin/mksh>
0
</bin/pdksh>
0
</bin/posh>
0
</bin/sh>
0
</bin/yash>
0
</bin/zsh>
</opt/schily/bin/osh>
0

kshऔर zshआउटपुट नहीं हुआ क्योंकि इन गोले में पाइप का अंतिम भाग सब-शेल के बजाय वर्तमान शेल में निष्पादित किया गया था। रिटर्न स्टेटमेंट ने वर्तमान शेल वातावरण को प्रभावित किया, जिसे फ़ंक्शन कहा जाता है, फ़ंक्शन को बिना किसी मुद्रण के तुरंत वापस कर देता है।

इंटरैक्टिव सत्र में, bashकेवल त्रुटि की रिपोर्ट करें, लेकिन शेल को समाप्त नहीं किया, schily's oshत्रुटि की सूचना दी और शेल को समाप्त कर दिया:

$ for s in /bin/*sh; do printf '<%s>\n' $s; $s -ci 'return 1; echo 1'; done
</bin/bash>
bash: return: can only `return' from a function or sourced script
1
</bin/dash>
</bin/ksh>
</bin/lksh>
</bin/mksh>
</bin/pdksh>
</bin/posh>
</bin/sh>
</bin/yash>
</bin/zsh>
</opt/schily/bin/osh>
$ cannot return when not in function

( zshइंटरेक्टिव सत्र और आउटपुट में टर्मिनल समाप्त नहीं होता है bash, yashऔर schily's oshत्रुटि की सूचना दी है लेकिन शेल समाप्त नहीं किया है)


1
यह तर्क दिया जा सकता है कि यहां एक समारोह के अंदरreturn इसका उपयोग किया जाता है।
jlliagre

1
@jlliagre: यदि आप सुनिश्चित नहीं हैं कि क्या मतलब है, returnअंदर उपयोग किया गया subshell अंदर समारोह , सिवाय kshऔर zsh
cuonglm

2
मेरा मतलब है कि एक सबशेल के अंदर जो कि एक फंक्शन के अंदर होता है, का मतलब यह नहीं है कि फंक्शन के बाहर होने का मतलब यह नहीं है, यानी स्टैंडर्ड स्टेट्स के पाइपलाइन कंपोनेंट्स में कुछ भी उस फंक्शन के बाहर नहीं माना जाना चाहिए, जहां वे स्थित हैं। यह ओपन ग्रुप द्वारा स्पष्ट किया जाना चाहिए।
जलियागरे

3
मेरे ख़्याल से नहीं। वह फंक्शन के बाहर है। शेल जिसे फ़ंक्शन कहा जाता है और उपधारा जो रिटर्न निष्पादित करता है, अलग-अलग हैं।
congonglm

मैं आपके तर्क को समझता हूं जो इस मुद्दे को ठीक से समझाता है, मेरी बात पोसिक्स मानक में वर्णित शेल व्याकरण के अनुसार है, पाइपलाइन यौगिक-सूची का एक हिस्सा है जो यौगिक-कमांड का हिस्सा है जो फ़ंक्शन का शरीर है। कहीं भी यह नहीं कहा गया है कि पाइपलाइन घटकों को फ़ंक्शन के बाहर माना जाता है। जैसे अगर मैं एक कार में हूँ और वह कार गैरेज में खड़ी है, तो मैं मान सकता हूँ कि मैं उस गैरेज में हूँ ;-)
jlliagre

4

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

foo(){ x=42; : | x=3; echo "x==$x";}

वैसे, वापसी काम कर रही है, लेकिन यह उपधारा से लौटती है। फिर से आप यह देख सकते हैं:

foo(){ : | return 1; echo$?; echo "This should not be printed.";}

निम्नलिखित उत्पादन करेगा:

1
This should not be printed.

इसलिए रिटर्न स्टेटमेंट सही तरीके से सब्मिट से बाहर निकल गया


2
इसलिए, फ़ंक्शन से बाहर निकलने के लिए, उपयोग करें foo(){ : | return 1 || return 2; echo$?; echo "This should not be printed.";}; foo; echo $?और आपको इसका परिणाम मिलेगा 2। लेकिन स्पष्टता के लिए मैं होगा return 1हो exit 1
संदिग्ध

वैसे, क्या इस तथ्य के लिए कुछ पुष्टिकरण है कि एक पाइपलाइन के सभी सदस्यों (सभी नहीं बल्कि एक) को उप-धाराओं में निष्पादित किया जाता है?
इंनिस मिसी

@IncnisMrsi: jlliagre का उत्तर देखें ।
स्कॉट

1

अधिक सामान्य उत्तर यह है कि बैश और कुछ अन्य गोले आमतौर पर एक पाइपलाइन के सभी तत्वों को अलग-अलग प्रक्रियाओं में डालते हैं। यह उचित है जब कमांड लाइन है

कार्यक्रम 1 | कार्यक्रम 2 | कार्यक्रम 3

चूंकि कार्यक्रम आम तौर पर वैसे भी (जब तक आप कहते हैं ) अलग-अलग प्रक्रियाओं में चलाए जाते हैं । लेकिन यह एक आश्चर्य के रूप में आ सकता हैexec program

कमांड 1 | कमांड 2 | आज्ञा 

जहाँ कुछ या सभी कमांड बिल्ट-इन कमांड होते हैं। तुच्छ उदाहरणों में शामिल हैं:

$ a=0
$ echo | a=1
$ echo "$a"
0
$ cd /
$ echo | cd /tmp
$ pwd
/

थोड़ा और यथार्थवादी उदाहरण है

$ t=0
$ ps | while read pid rest_of_line
> do
>     : $((t+=pid))
> done
$ echo "$t"
0

जहां पूरे while... do... done, और उसके परिवर्तनों तो पाश एक उपप्रक्रिया में डाल दिया है करने के लिए tलूप समाप्त होने के बाद मुख्य खोल को दिखाई नहीं देते। और ठीक यही आप कर रहे हैं - एक whileलूप में पाइपिंग करना , जिससे लूप एक सबशेल के रूप में चल सकता है, और फिर सब से वापस आने की कोशिश कर रहा है।

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