, लूप ’पढ़ते समय मेरा वेरिएबल लोकल में क्यों है, लेकिन दूसरे में समान लूप नहीं है?


25

$xनीचे दिए गए स्निपेट्स से मुझे अलग-अलग मान क्यों मिलते हैं?

#!/bin/bash

x=1
echo fred > junk ; while read var ; do x=55 ; done < junk
echo x=$x 
#    x=55 .. I'd expect this result

x=1
cat junk | while read var ; do x=55 ; done
echo x=$x 
#    x=1 .. but why?

x=1
echo fred | while read var ; do x=55 ; done
echo x=$x 
#    x=1  .. but why?

जवाबों:


26

सही स्पष्टीकरण पहले से ही jsbillings और geekosaur द्वारा दिया गया है , लेकिन मुझे उस पर थोड़ा विस्तार करना चाहिए।

अधिकांश गोले में, बैश सहित, एक पाइप लाइन का प्रत्येक भाग एक उप-भाग में चलता है, इसलिए शेल की आंतरिक स्थिति में परिवर्तन (जैसे चर सेट करना) पाइप लाइन के उस खंड तक ही सीमित रहता है। केवल एक जानकारी जो आप एक उपधारा से प्राप्त कर सकते हैं वह यह है कि यह आउटपुट (मानक आउटपुट और अन्य फ़ाइल डिस्क्रिप्टर के लिए) और इसका निकास कोड (जो 0 और 255 के बीच की संख्या है)। उदाहरण के लिए, निम्नलिखित स्निपेट प्रिंट 0:

a=0; a=1 | a=2; echo $a

Ksh में (AT & T कोड से व्युत्पन्न वेरिएंट, pdksh / mksh वेरिएंट नहीं) और zsh, पाइप लाइन में अंतिम आइटम को पेरेंट शेल में निष्पादित किया जाता है। (POSIX दोनों व्यवहारों की अनुमति देता है।) तो प्रिंट 2 से ऊपर का स्निपेट।

एक उपयोगी मुहावरे में लूप की निरंतरता को शामिल करना है (या आपके पास पाइप लाइन के दाईं ओर जो कुछ भी है, लेकिन पाइपलाइन में थोड़ी देर का लूप वास्तव में यहां आम है):

cat junk | {
  while read var ; do x=55 ; done
  echo x=$x 
}

1
धन्यवाद गिल्स .. यह एक = 0; a = 1 | a = 2 एक बहुत ही स्पष्ट तस्वीर देता है .. और न केवल आंतरिक स्थिति के स्थानीयकरण, बल्कि यह भी कि पाइप लाइन के माध्यम से वास्तव में कुछ भी भेजने की आवश्यकता नहीं है (निकास कोड (?) के अलावा) .. अपने आप में? एक पाइप में एक दिलचस्प अंतर्दृष्टि है ... मैंने अपनी स्क्रिप्ट के साथ चलने का प्रबंधन किया < <(locate -ber ^\.tag$), मूल थोड़ा अस्पष्ट उत्तर और गीकॉउसर और ग्लेन जैकमैन की कॉमेनेट्स के लिए धन्यवाद। मैं शुरू में उत्तर को स्वीकार करने के बारे में दुविधा में था, लेकिन शुद्ध परिणाम बहुत स्पष्ट था, विशेष रूप से jsbillings अनुवर्ती टिप्पणी के साथ :)
पीटर।

ऐसा लगता है कि मैं एक समारोह में पहुंच गया, इसलिए मैंने इसके अंदर कुछ चर और परीक्षण चलाए और इसने महान काम किया, thx!
कुंभ शक्ति

8

आप एक चर क्षेत्र समस्या में चल रहे हैं। पाइप के दाईं ओर स्थित लूप में परिभाषित चर का अपना स्थानीय क्षेत्र संदर्भ होता है, और चर में परिवर्तन लूप के बाहर दिखाई नहीं देगा। जबकि लूप अनिवार्य रूप से एक उपधारा है जो शेल पर्यावरण का एक COPY हो जाता है, और शेल के अंत में पर्यावरण में कोई भी परिवर्तन खो जाता है। यह StackOverflow प्रश्न देखें ।

अद्यतन : मैंने महत्वपूर्ण तथ्य को इंगित करने के लिए उपेक्षा की है कि लूप के साथ स्वयं के उपखंड होने के कारण यह एक पाइप का समापन बिंदु होने के कारण, मैंने उत्तर में इसे अपडेट किया है।


@jsbillings .. ठीक है, कि दो अंतिम स्निपेट बताते हैं, लेकिन यह पहले नहीं समझाता है, जहां लूप में $ x सेट का मूल्य 55 के रूप में है, जबकि 'लूप' के दायरे से परे है
पीटर

5
@ fred.bear: यह whileलूप को एक पाइप लाइन के टेल एंड के रूप में चला रहा है जो इसे सबशेल में फेंकता है।
गाइकोसॉर

2
यह वह जगह है जहाँ बैश प्रक्रिया प्रतिस्थापन खेल में आता है। इसके बजाय blah|blah|while read ..., आप कर सकते हैंwhile read ...; done < <(blah|blah)
ग्लेन जैकमैन

1
@geekosaur: उन विवरणों को भरने के लिए धन्यवाद, जिन्हें मैंने अपने उत्तर में शामिल करने की उपेक्षा की।
jsbillings

1
-1 क्षमा करें लेकिन यह उत्तर गलत है। यह बताता है कि यह सामान कई प्रोग्रामिंग भाषाओं में काम करता है लेकिन शेल में नहीं। @ गिल्स, नीचे, यह सही है।
jpc

6

जैसा कि अन्य उत्तरों में उल्लेख किया गया है , एक पाइपलाइन के हिस्से उप-भागों में चलते हैं, इसलिए वहां किए गए संशोधन मुख्य शेल को दिखाई नहीं देते हैं।

यदि हम सिर्फ बैश पर विचार करते हैं, तो cmd | { stuff; more stuff; }संरचना के अतिरिक्त दो अन्य वर्कअराउंड हैं :

  1. प्रक्रिया प्रतिस्थापन से इनपुट पुनर्निर्देशित करें :

    while read var ; do x=55 ; done < <(echo fred)
    echo "$x"

    इसमें कमांड से आउटपुट <(...)ऐसा प्रतीत होता है जैसे कि यह एक नामित पाइप था।

  2. lastpipeविकल्प है, जो ksh तरह बैश काम करता है, और मुख्य खोल प्रक्रिया में पाइप लाइन के अंतिम भाग चलाता है। हालांकि यह केवल तभी काम करता है जब नौकरी नियंत्रण अक्षम हो, अर्थात एक इंटरैक्टिव शेल में नहीं:

    bash -c '
      shopt -s lastpipe
      echo fred | while read var ; do x=55 ; done; 
      echo "$x"
    '

    या

    bash -O lastpipe -c '
      echo fred | while read var ; do x=55 ; done; 
      echo "$x"
    '

प्रक्रिया प्रतिस्थापन ksh और zsh में भी निश्चित रूप से समर्थित है। लेकिन चूंकि वे पाइप लाइन के अंतिम हिस्से को वैसे भी मुख्य शेल में चलाते हैं, इसलिए इसे वर्कअराउंड के रूप में उपयोग करना वास्तव में आवश्यक नहीं है।


0
#!/bin/bash
set -x

# prepare test data.
mkdir -p ~/test_var_global
cd ~/test_var_global
echo "a"> core.1
echo "b"> core.2
echo "c"> core.3


var=0

coreFiles=$(find . -type f -name "core*")
while read -r file;
do
  # perform computations on $i
  ((var++))
done <<EOF
$coreFiles
EOF

echo $var

Result:
...
+ echo 3
3

यह काम कर सकता है।

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