2008-08-05 32 views
16

क्या कोई इस व्यवहार को समझा सकता है? रनिंग: कुछ भी नहीं किया जा रहा है ouput मेंशैल स्क्रिप्टिंग इनपुट पुनर्निर्देशन विषमता

#!/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 

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

उत्तर

8

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

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

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

hello 
world 
3

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

इस आदेश

$ echo $$;cat | read a 
10637 

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

|      |-bash(10637)-+-bash(10786) 
    |      |    `-cat(10785) 
5

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

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

var1=$(echo "Hello") 
echo var1 

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

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

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

+2

यह खोल की पसंद पर निर्भर करता है। Ksh93 प्रक्रिया ओवरहेड को सीमित करने के लिए डिज़ाइन किया गया था।यह एक पाइपलाइन * अंदर * invoking खोल प्रक्रिया के अंतिम तत्व भी चलाता है, जिससे राज्य को संरक्षित किया जाता है। –

+1

बैश 4.2 ने एक ही काम करने का विकल्प पेश किया। नौकरी नियंत्रण बंद करें ('सेट + एम') और' lastpipe' विकल्प ('shopt -s lastpipe') सेट करें। – chepner

+0

मैं उसमें देख लूंगा। धन्यवाद! –

9

यह पहले से ही सही ढंग से उत्तर दिया गया है, लेकिन समाधान अभी तक नहीं बताया गया है। 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 क्योंकि इस तरह छोटे विवरण के एक बेहतर प्रोग्रामिंग खोल है। (बैश मेरी राय में बेहतर इंटरैक्टिव खोल है।)

3

पोस्ट का सही उत्तर दिया गया है, लेकिन मैं एक वैकल्पिक एक लाइनर पेश करना चाहता हूं जो शायद कुछ उपयोग हो सकता है।

गूंज से अंतरिक्ष अलग मान निर्दिष्ट (या उस बात के लिए stdout) चर शेल के लिए, आप खोल सरणियों उपयोग करने पर विचार कर सकता है:

$ var=($(echo 'hello world')) 
$ echo ${var[0]} 
hello 
$ echo ${var[1]} 
world 

इस उदाहरण वर में एक सरणी है और सामग्री का उपयोग कर पहुँचा जा सकता है निर्माण $ {var [index]}, जहां अनुक्रमणिका सरणी अनुक्रमणिका है (0 से शुरू होती है)।

इस तरह से आप कई पैरामीटर प्राप्त कर सकते हैं जैसे आप प्रासंगिक सरणी अनुक्रमणिका को असाइन करना चाहते हैं।

+2

अच्छा समाधान। यह बैश में बहुत अच्छा काम करता है, लेकिन यह डैश खोल में काम नहीं करता है। स्वच्छ उदाहरणों के लिए –

8
read var1 var2 < <(echo "hello world") 
3

प्रयास करें:

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

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

result=`echo "hello world"` 
read var1 var2 <<EOF 
$result 
EOF 
echo $var1 
echo $var2 
10
#!/bin/sh 
echo "hello world" | read var1 var2 
echo $var1 
echo $var2 

क्योंकि पाइपलाइनों एक subshell अंदर उनके घटकों के बारे में चलाने के लिए कोई उत्पादन पैदा करता है। सब्सक्रिप्ल्स माता-पिता शैल के चर के प्रति प्रतियों को प्रतिस्थापित करने के बजाय प्रतियां प्राप्त करते हैं। इस प्रयास करें:

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

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

अब इस प्रयास करें:

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

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

अब इस प्रयास करें:

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

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

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

+1

+1 - अच्छा, पठनीय कोड –

4

इस मुद्दे पर मेरे ले (बैश का प्रयोग करके):

read var1 var2 <<< "hello world" 
echo $var1 $var2