बैश: वेरिएबल लूप पढ़ते समय अंत में वैल्यू खो देता है


36

मुझे अपनी एक शेल स्क्रिप्ट में समस्या है। कुछ सहयोगियों से पूछा, लेकिन वे सभी अपने सिर हिलाते हैं (कुछ खरोंचने के बाद), इसलिए मैं यहां जवाब देने आया हूं।

मेरी समझ के अनुसार निम्नलिखित शेल स्क्रिप्ट को अंतिम पंक्ति के रूप में "काउंट 5 है" प्रिंट करना चाहिए। सिवाय इसके कि नहीं। यह "गणना 0 है" प्रिंट करता है। यदि "पढ़ते समय" को किसी अन्य प्रकार के लूप से बदल दिया जाता है, तो यह ठीक काम करता है। यहाँ स्क्रिप्ट है:

इको "1"> input.data
इको "2" >> input.data
इको "3" >> input.data
इको "4" >> input.data
इको "5" >> input.data

CNT = 0 

बिल्ली input.data | पढ़ते समय;
करना
  CNT ++;
  इको "$ सीएनटी की गिनती"
किया हुआ 
इको "काउंट इज $ सीएनटी"

ऐसा क्यों होता है और मैं इसे कैसे रोक सकता हूं? मैंने इसे डेबियन लेनी और स्क्वीज़ में आज़माया है, एक ही परिणाम (यानी 3.2.39 को मारना और 4.1.5 को बैश करना। मैं शेल स्क्रिप्ट विज़ार्ड नहीं होने के लिए पूरी तरह से स्वीकार करता हूं, इसलिए किसी भी संकेत की सराहना की जाएगी।

जवाबों:


30

देखें तर्क @ बैश FAQ प्रविष्टि # 24: "मैं एक लूप में चर सेट करता हूं। लूप के समाप्त होने के बाद वे अचानक क्यों गायब हो जाते हैं? या, मैं पढ़ने के लिए डेटा पाइप क्यों नहीं कर सकता?" (हाल ही में यहां संग्रहीत )।

सारांश: यह केवल बैश 4.2 और उसके बाद के संस्करण से समर्थित है। यदि आप बैश का उपयोग कर रहे हैं, तो आपको पाइप के बजाय कमांड प्रतिस्थापन जैसे विभिन्न तरीकों का उपयोग करने की आवश्यकता है।


आपको बोनस मिलता है, क्योंकि आपके जवाब ने मुझे विकल्पों की सबसे विस्तृत श्रृंखला प्रदान की है।
वुल्फगैंग्स्ज़

5
लिंक मर चुका है। यही कारण है कि लिंक-केवल उत्तर खराब हैं। कम से कम यहाँ जवाब को संक्षेप में दें।
रुडोल्फबाइकर

भगवान, अभी तक एक बार जहां ksh बस इतना बेहतर है ... क्यों, बस क्यों हर कोई चारों ओर झुका था।
फ्लोरियन हीगल

@FlorianHeigl: क्या आप दावा कर रहे हैं कि ksh एक सच्चा खोल है?
इग्नासियो वाज़केज़-अब्राम्स

@ IgnacioVazquez-Abrams नहीं, लेकिन मैं दावा करता हूं कि बैश में लूप को संभालना एक भयानक पीटीए है। लूप हैंडलिंग 1993 की कार्यक्षमता को पकड़ने से लेकर उसे चलाने तक का लंबा धावक था। अन्य चीजों को हैंडल करने की सुविधा मिलती है जहां (1993 भी) बिल्ट हैंडलर सरल और सक्षम था, कुछ आप अभी भी प्राप्त नहीं कर सकते हैं जब तक कि डॉकटॉप का उपयोग नहीं किया जाता है। मैं दावा कर रहा हूं कि बैश ने 20 साल से अधिक समय तक अपने आप को वक्र के पीछे रखा है, आग्रहपूर्वक और इस बुरे समय पर खर्च किए गए लाखों या खराब गेटटॉप के उपयोग की मात्रा माप से परे है - केवल इसलिए स्वीकार किया जाता है क्योंकि अधिकांश लोग कभी नहीं जान पाएंगे।
फ्लोरियन हेगल

30

यह एक 'आम' गलती की तरह है। पाइप सबशेल्स बनाते हैं, इसलिए while readआपकी स्क्रिप्ट की तुलना में एक अलग शेल पर चल रहा है, जिससे आपका CNTवेरिएबल कभी भी नहीं बदलता है (केवल पाइप सबहेल्ड के अंदर वाला)।

इसे ठीक करने के echoलिए उप- समूह के साथ अंतिम समूह बनाएं while(इसे ठीक करने के कई अन्य तरीके हैं, यह एक है। इयान और इग्नासियो के जवाब अन्य हैं।)

CNT=0

 cat input.data | ( while read 
do
  let CNT++;
  echo "Counting to $CNT"
done 
echo "Count is $CNT" )

लंबी व्याख्या:

  1. आप CNTअपनी स्क्रिप्ट पर मूल्य 0 घोषित करते हैं;
  2. इस पर एक सबशेल शुरू किया गया |है while read;
  3. आपके $CNTचर को मान 0 के साथ सबशेल में निर्यात किया जाता है;
  4. सबशेल गिनता है और CNTमान को 5 तक बढ़ाता है ;
  5. सबशेल समाप्त होता है, चर और मान नष्ट हो जाते हैं (वे कॉलिंग प्रक्रिया / स्क्रिप्ट पर वापस नहीं आते हैं)।
  6. आप echoअपना मूल CNTमान ० है।

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

उत्कृष्ट जवाब, दुर्भाग्य से मैं केवल एक स्वीकृति बोनस दे सकता हूं। माफ़ कीजिये।
वुल्फगैंग्ज़

10

यह काम

CNT=0 

while read ;
do
  let CNT++;
  echo "Counting to $CNT"
done <input.data
echo "Count is $CNT"

मुझे वह पसंद है, स्मार्ट तरीका क्योंकि आप जानते हैं कि आवश्यक डेटा कहां है और केवल इसे वापस लाने की आवश्यकता है। यदि आप उच्च कौशल समाधान नहीं जानते हैं, तो आप हमेशा "फ़ाइल पढ़ सकते हैं" हाहाहा। आपके लिए +1।
m3nda

1
इसे पढ़ने वाला कोई भी व्यक्ति इस बात से अवगत रहें कि Iain द्वारा प्रदान किया गया समाधान केवल तभी काम करता है जब आपके पास आपकी स्क्रिप्ट स्पष्ट रूप से बैश हो, पहली पंक्ति होने से: #! / Bin / bash और वह: #! / Bin / sh काम नहीं करेगा।
रोड शो

1
दिलचस्प, पहला उदाहरण मैंने कभी देखा कि बिल्ली का एक बेकार उपयोग वास्तव में कोड को काम करने से रोकता है । वैसे @Radadl के अनुसार, यहां केवल बैशिज़्म ही वह रेखा है let CNT++जिसके बजाय POSIX- अनुरूप अंकगणितीय विस्तारCNT="$((CNT+1))" का उपयोग किया जाना चाहिए । बाकी यह पहले से ही पोर्टेबल है।
वाइल्डकार्ड

6

इसके बजाय उप-शेल में डेटा पास करने की कोशिश करें, जैसे कि यह लूप से पहले की फाइल है। यह लेन के समाधान के समान है, लेकिन मानता है कि आप कुछ रुक-रुक कर फाइल नहीं चाहते हैं:

total=0
while read var
do
  echo "variable: $var"
  ((total+=var))
done < <(echo 45) #output from a command, script, or function
echo "total: $total"
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.