शैल स्क्रिप्टिंग इनपुट पुनर्निर्देशन विषमताएं

क्या कोई इस व्यवहार को समझा सकता है? चल रहा है:

#!/bin/sh
echo "hello world" | read var1 var2
echo $var1
echo $var2

परिणामस्वरूप आउटपुट नहीं होता है, जबकि:

#!/bin/sh
echo "hello world" > test.file
read var1 var2 < test.file
echo $var1
echo $var2

अपेक्षित आउटपुट का उत्पादन करता है:

hello
world

पाइप को एक चरण में नहीं करना चाहिए क्या परीक्षण के लिए पुनर्निर्देशन दूसरे उदाहरण में किया गया है? मैंने डैश और बैश गोले दोनों के साथ एक ही कोड की कोशिश की और उन दोनों से एक ही व्यवहार प्राप्त किया।

0
जोड़ा संपादित
विचारों: 4

7 उत्तर

ऐसा इसलिए है क्योंकि पाइप संस्करण एक सबहेल बना रहा है, जो वैरिएबल को अपनी स्थानीय जगह में पढ़ता है जो तब सबहेल समाप्त होने पर नष्ट हो जाता है।

इस आदेश का निष्पादन करें

$ echo $$;cat | read a
10637

और चल रही प्रक्रियाओं को देखने के लिए pstree -p का उपयोग करें, आप अपने मुख्य खोल से एक अतिरिक्त खोल लटकते देखेंगे।

    |                       |-bash(10637)-+-bash(10786)
    |                       |             `-cat(10785)
0
जोड़ा

ठीक है, मैंने इसे समझ लिया!

यह पकड़ने के लिए एक कठिन बग है, लेकिन जिस तरह से पाइप को खोल से नियंत्रित किया जाता है। एक पाइपलाइन का हर तत्व एक अलग प्रक्रिया में चलाता है। जब पठन कमांड var1 और var2 सेट करता है, तो यह उन्हें अपने स्वयं के सबहेल सेट करता है, न कि पैरेंट खोल। तो जब सबहेल निकलता है, var1 और var2 के मान खो जाते हैं। हालांकि, आप कोशिश कर सकते हैं

var1=$(echo "Hello")
echo var1

जो अपेक्षित उत्तर देता है। दुर्भाग्यवश यह केवल एकल चर के लिए काम करता है, आप एक समय में कई सेट नहीं कर सकते हैं। एक समय में एकाधिक चर सेट करने के लिए आपको या तो एक चर में पढ़ना चाहिए और इसे कई चरों में काटना चाहिए या इस तरह कुछ उपयोग करना चाहिए:

set -- $(echo "Hello World")
var1="$1" var2="$2"
echo $var1
echo $var2

जबकि मैं मानता हूं कि यह एक पाइप का उपयोग करने के रूप में सुरुचिपूर्ण नहीं है, यह काम करता है। बेशक आपको यह ध्यान में रखना चाहिए कि पढ़ना फाइलों से चरों में पढ़ने के लिए था, इसलिए इसे मानक इनपुट से पढ़ना थोड़ा कठिन होना चाहिए।

0
जोड़ा
बैश 4.2 ने एक ही काम करने का विकल्प पेश किया। नौकरी नियंत्रण बंद करें ( set + m ) और lastpipe विकल्प ( shopt -s lastpipe ) सेट करें।
जोड़ा लेखक chepner, स्रोत
यह खोल की पसंद पर निर्भर करता है। Ksh93 प्रक्रिया ओवरहेड को सीमित करने के लिए डिज़ाइन किया गया था। यह एक पाइपलाइन अंदर आविष्कार खोल प्रक्रिया का अंतिम तत्व भी चलाता है, जिससे राज्य को संरक्षित किया जाता है।
जोड़ा लेखक Henk Langeveld, स्रोत
मैं उसमें देख लूंगा। धन्यवाद!
जोड़ा लेखक Henk Langeveld, स्रोत

bash का हालिया जोड़ा lastpipe विकल्प है, जो कि अंतिम नियंत्रण में चलने के लिए पाइपलाइन में अंतिम कमांड को चलाने के लिए अंतिम शेल को अनुमति देता है, जब सबकुछ नहीं होता है, जब नौकरी नियंत्रण निष्क्रिय होता है।

#!/bin/bash
set +m      # Deactiveate job control
shopt -s lastpipe
echo "hello world" | read var1 var2
echo $var1
echo $var2

वास्तव में उत्पादन होगा

hello
world
0
जोड़ा

प्रयत्न:

echo "hello world" | (read var1 var2 ; echo $var1 ; echo $var2 )

समस्या, जैसा कि कई लोगों ने कहा है, यह है कि var1 और var2 एक सबहेल वातावरण में बनाए जाते हैं जो उस सबहेल से निकलते समय नष्ट हो जाते हैं। उपर्युक्त को तब तक नष्ट करने से बचाता है जब तक परिणाम गूंज नहीं किया जाता है। एक और समाधान है:

result=`echo "hello world"`
read var1 var2 <
0
जोड़ा

इसका पहले से ही सही उत्तर दिया गया है, लेकिन समाधान अभी तक नहीं बताया गया है। Ksh का प्रयोग करें, बाश नहीं। की तुलना करें:

$ echo 'echo "hello world" | read var1 var2
echo $var1
echo $var2' | bash -s

सेवा मेरे:

$ echo 'echo "hello world" | read var1 var2
echo $var1
echo $var2' | ksh -s
hello
world

ksh इस तरह की छोटी niceties की वजह से एक बेहतर प्रोग्रामिंग खोल है। (बैश मेरी राय में बेहतर इंटरैक्टिव खोल है।)

0
जोड़ा
read var1 var2 < <(echo "hello world")
0
जोड़ा
#!/bin/sh
echo "hello world" | read var1 var2
echo $var1
echo $var2

कोई उत्पादन नहीं करता है क्योंकि पाइपलाइनें अपने प्रत्येक घटकों को सबहेल के अंदर चलाती हैं। उन्हें साझा करने के बजाय, मूल शैल के चर के सब्सक्रिप्शन प्रतियां प्राप्त करें । इसे इस्तेमाल करे:

#!/bin/sh
foo="contents of shell variable foo"
echo $foo
(
    echo $foo
    foo="foo contents modified"
    echo $foo
)
echo $foo

कोष्ठक कोड के एक क्षेत्र को परिभाषित करते हैं जो एक सबहेल में चलाया जाता है, और $ foo उनके अंदर संशोधित होने के बाद अपने मूल मूल्य को बरकरार रखता है।

अब इसे आज़माएं:

#!/bin/sh
foo="contents of shell variable foo"
echo $foo
{
    echo $foo
    foo="foo contents modified"
    echo $foo
}
echo $foo

ब्रेसिज़ पूरी तरह से समूह के लिए हैं, कोई सबहेल नहीं बनाया गया है, और ब्रेसिज़ के अंदर संशोधित $ foo वही $ foo उनके बाहर संशोधित है।

अब इसे आज़माएं:

#!/bin/sh
echo "hello world" | {
    read var1 var2
    echo $var1
    echo $var2
}
echo $var1
echo $var2

ब्रेसिज़ के अंदर, रीड बिल्टिन $ var1 और $ var2 को ठीक से बनाता है और आप देख सकते हैं कि वे प्रतिबिंबित हो जाते हैं। ब्रेसिज़ के बाहर, वे अब और मौजूद नहीं हैं। ब्रेसिज़ के भीतर सभी कोड एक सबहेल में चलाया गया है क्योंकि यह पाइपलाइन का एक घटक है

आप ब्रेसिज़ के बीच कोड की मनमानी मात्रा डाल सकते हैं, ताकि आप इस पाइपिंग-इन-ए-ब्लॉक निर्माण का उपयोग कर सकें जब भी आपको शेल स्क्रिप्ट का एक ब्लॉक चलाने की आवश्यकता होती है जो किसी और चीज के आउटपुट को पार करती है।

0
जोड़ा
स्वच्छ उदाहरणों के लिए +1 - अच्छा, पठनीय कोड
जोड़ा लेखक Henk Langeveld, स्रोत