एक पाइप से एक शेल चर में मूल्यों को पढ़ें


205

मैं स्टड से डेटा को प्रोसेस करने के लिए बैश होने की कोशिश कर रहा हूं जो कि पाइप हो जाता है, लेकिन भाग्य नहीं। मेरा मतलब है कि निम्नलिखित में से कोई भी काम नहीं है:

echo "hello world" | test=($(< /dev/stdin)); echo test=$test
test=

echo "hello world" | read test; echo test=$test
test=

echo "hello world" | test=`cat`; echo test=$test
test=

जहां मैं चाहता हूं कि आउटपुट हो test=hello world। मैंने "" उद्धरण डालने की कोशिश की है "$test"जो आसपास काम नहीं करता है।


1
आपका उदाहरण .. गूंज "नमस्ते दुनिया" | परीक्षण पढ़ें; इको टेस्ट = $ टेस्ट ने मेरे लिए ठीक काम किया .. परिणाम: टेस्ट = हैलो वर्ल्ड; इसके तहत कौन सा वातावरण चल रहा है? मैं बैश 4.2 का उपयोग कर रहा हूं ..
alex.pilon

क्या आप एक ही बार में कई लाइनें चाहते हैं? आपका उदाहरण केवल एक पंक्ति दिखाता है, लेकिन समस्या का वर्णन अस्पष्ट है।
चार्ल्स डफी

2
@ alex.pilon, मैं बैश संस्करण 4.2.25 चला रहा हूं, और उसका उदाहरण मेरे लिए भी काम नहीं करता है। हो सकता है कि यह बैश रनटाइम विकल्प या पर्यावरण चर का मामला हो? मैंने उदाहरण दिया है कि न तो Sh के साथ काम करता है, न ही Bash, Sh के साथ संगत होने का प्रयास कर सकता है?
Hibou57

2
@ Hibou57 - मैंने इसे 4.3.25 को फिर से करने की कोशिश की और यह अब काम नहीं करता है। इस पर मेरी स्मृति फ़र्ज़ी है और मुझे यकीन नहीं है कि मैंने इसे काम करने के लिए क्या किया होगा।
अलेक्स.पिलोन

2
@ Hibou57 @ alex.pilon एक पाइप में पिछले cmd प्रभावित करना चाहिए bash4 में वार्स> = 4.2 के साथ shopt -s lastpipe- tldp.org/LDP/abs/html/bashver4.html#LASTPIPEOPT
IMZ - इवान Zakharyaschev

जवाबों:


162

उपयोग

IFS= read var << EOF
$(foo)
EOF

आप कर सकते हैं चाल readइस तरह एक पाइप से स्वीकार करने में:

echo "hello world" | { read test; echo test=$test; }

या यहां तक ​​कि इस तरह एक समारोह लिखें:

read_from_pipe() { read "$@" <&0; }

लेकिन इसका कोई मतलब नहीं है - आपके चर असाइनमेंट अंतिम नहीं हो सकते हैं! एक पाइपलाइन एक उपधारा को स्पॉन कर सकती है, जहां पर्यावरण को मूल्य से विरासत में मिला है, न कि संदर्भ द्वारा। यही कारण है कि readएक पाइप से इनपुट के साथ परेशान नहीं होता है - यह अपरिभाषित है।

FYI करें, http://www.etalabs.net/sh_tricks.html बॉर्न शेल की विषमताओं और विषमताओं से लड़ने के लिए आवश्यक cruft का एक निफ्टी संग्रह है, श।


आप इसके बजाय यह करके असाइनमेंट को अंतिम बना सकते हैं: `परीक्षण =` `प्रतिध्वनि" हैलो वर्ल्ड "| {टेस्ट पढ़ें; इको $ टेस्ट; } `` `
कॉम्फोलियो

2
आइए इसे फिर से आज़माएं (जाहिरा तौर पर इस मार्कअप में test=`echo "hello world" | { read test; echo $test; }`
पीछे हटने

क्या मैं पूछ सकता हूं कि आपने उन दो आदेशों को समूहीकृत {}करने के बजाय क्यों इस्तेमाल किया ()?
जुरगेन पॉल

4
चाल readपाइप से इनपुट लेने में नहीं है , लेकिन उसी शेल में चर का उपयोग करने में जो निष्पादित करता है read
शेपनर

1
bash permission deniedइस समाधान का उपयोग करने का प्रयास करते समय मिला । मेरा मामला काफी अलग है, लेकिन मुझे इसके लिए कहीं भी जवाब नहीं मिला, जो मेरे लिए काम करता था (एक अलग उदाहरण लेकिन इसी तरह का उपयोग) pip install -U echo $(ls -t *.py | head -1):। मामले में, किसी को कभी भी इसी तरह की समस्या है और मेरे जैसे जवाब पर ठोकर खाई है।
ivan_bilan

111

यदि आप बहुत से डेटा पढ़ना चाहते हैं और प्रत्येक पंक्ति पर अलग से काम करना चाहते हैं तो आप कुछ इस तरह से उपयोग कर सकते हैं:

cat myFile | while read x ; do echo $x ; done

यदि आप लाइनों को कई शब्दों में विभाजित करना चाहते हैं तो आप x के स्थान पर कई चर का उपयोग कर सकते हैं:

cat myFile | while read x y ; do echo $y $x ; done

वैकल्पिक रूप से:

while read x y ; do echo $y $x ; done < myFile

लेकिन जैसे ही आप इस तरह की चीज़ के साथ वास्तव में कुछ भी करने की इच्छा करना शुरू करते हैं, तो आप बेहतर तरीके से कुछ स्क्रिप्टिंग भाषा के लिए जा रहे हैं जैसे कि पर्ल जहां आप कुछ इस तरह की कोशिश कर सकते हैं:

perl -ane 'print "$F[0]\n"' < myFile

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


8
"वैकल्पिक रूप से" सही तरीका है। कोई UUoC और कोई उपधारा नहीं। बशफाक / 024 देखें ।
अगली सूचना तक रोक दिया गया।

43

readएक पाइप से नहीं पढ़ा जाएगा (या संभवतः परिणाम खो गया है क्योंकि पाइप एक उपखंड बनाता है)। हालाँकि, आप यहाँ Bash में स्ट्रिंग का उपयोग कर सकते हैं:

$ read a b c <<< $(echo 1 2 3)
$ echo $a $b $c
1 2 3

लेकिन जानकारी के लिए @ chepner का जवाब देखें lastpipe


अच्छा और सरल एक-लाइनर, समझने में आसान। इस उत्तर को और अधिक बढ़ाने की जरूरत है।
डेविड पार्क

42

यह एक और विकल्प है

$ read test < <(echo hello world)

$ echo $test
hello world

24
जो महत्वपूर्ण लाभ है वह <(..)यह $(..)है कि <(..)कॉल करने वाले को जितनी जल्दी आदेश दिया जाता है, उतनी जल्दी प्रत्येक लाइन लौटाता है। $(..)हालाँकि, कमांड के किसी भी आउटपुट को कॉल करने से पहले उसके सभी आउटपुट को पूरा करने और उत्पन्न करने के लिए प्रतीक्षा करता है।
डेरेक महार

37

मैं बैश में कोई विशेषज्ञ नहीं हूं, लेकिन मुझे आश्चर्य है कि यह प्रस्तावित क्यों नहीं किया गया है:

stdin=$(cat)

echo "$stdin"

एक-लाइनर सबूत है कि यह मेरे लिए काम करता है:

$ fortune | eval 'stdin=$(cat); echo "$stdin"'

4
ऐसा शायद इसलिए है क्योंकि "पढ़ा" एक बैश कमांड है, और बिल्ली एक अलग बाइनरी है जिसे एक उपप्रकार में लॉन्च किया जाएगा, इसलिए यह कम कुशल है।
dj_segfault

10
कभी-कभी सादगी और स्पष्टता ट्रम्प दक्षता :)
रोंडो

5
निश्चित रूप से सबसे सीधा-सीधा जवाब
drwatsoncode

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

@ डेल्ला वेल, बिल्कुल। कोई भी प्रोग्राम जो मानक इनपुट से पढ़ने की कोशिश करता है, अगर उसे कुछ नहीं खिलाया जाता है तो वह "लटका" होगा।
djanowski

27

bash4.2 lastpipeविकल्प का परिचय देता है, जो आपके कोड को एक उपधारा के बजाय वर्तमान शेल में एक पाइप लाइन में अंतिम कमांड को निष्पादित करके लिखित रूप में काम करने की अनुमति देता है।

shopt -s lastpipe
echo "hello world" | read test; echo test=$test

2
आह! बहुत अच्छा, यह। अगर एक इंटरैक्टिव खोल में परीक्षण, यह भी: "सेट + मीटर" (एक .sh स्क्रिप्ट में आवश्यक नहीं है)
XXL

14

शेल कमांड से एक बैश वैरिएबल में निहित पाइप के लिए सिंटैक्स है

var=$(command)

या

var=`command`

आपके उदाहरणों में, आप असाइनमेंट स्टेटमेंट में डेटा को पाइप कर रहे हैं, जिससे किसी इनपुट की उम्मीद नहीं है।


4
क्योंकि $ () को आसानी से घोंसला बनाया जा सकता है। JAVA_DIR = $ (dirname $ (readlink -f $ (java))) में सोचें, और इसे `के साथ आज़माएँ। आपको तीन बार भागने की आवश्यकता होगी!
एल्बफैन

8

पहला प्रयास काफी करीब था। इस विविधता को काम करना चाहिए:

echo "hello world" | { test=$(< /dev/stdin); echo "test=$test"; };

और आउटपुट है:

परीक्षण = नमस्ते दुनिया

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

ब्रेसिज़ के बिना, परीक्षण करने के लिए असाइनमेंट (पाइप के बाद) एक खोल में है, और गूंज "परीक्षण = $ परीक्षण" एक अलग शेल में है जो उस असाइनमेंट के बारे में नहीं जानता है। इसलिए आपको आउटपुट में "टेस्ट = हैलो वर्ल्ड" के बजाय "टेस्ट =" मिल रहा था।


8

मेरी नजर में बासी में स्टड से पढ़ने का सबसे अच्छा तरीका निम्नलिखित है, जो आपको इनपुट से पहले लाइनों पर काम करने देता है:

while read LINE; do
    echo $LINE
done < /dev/stdin

1
मैं यह पाने से पहले लगभग पागल हो गया। साझा करने के लिए बहुत बहुत धन्यवाद!
सामी डिंडाने

6

क्योंकि मैं इसके लिए गिरता हूं, मैं एक नोट छोड़ना चाहूंगा। मुझे यह धागा मिला, क्योंकि मुझे POSIX संगत होने के लिए एक पुरानी श स्क्रिप्ट को फिर से लिखना होगा। यह मूल रूप से इस तरह से पुनर्लेखन कोड द्वारा POSIX द्वारा पेश की गई पाइप / सबशेल समस्या को रोकने के लिए है:

some_command | read a b c

में:

read a b c << EOF
$(some_command)
EOF

और इस तरह कोड:

some_command |
while read a b c; do
    # something
done

में:

while read a b c; do
    # something
done << EOF
$(some_command)
EOF

लेकिन उत्तरार्द्ध खाली इनपुट पर समान व्यवहार नहीं करता है। पुराने नोटेशन के साथ, जबकि लूप खाली इनपुट पर दर्ज नहीं किया गया है, लेकिन पोसिक्स नोटेशन में है! मुझे लगता है कि यह EOF से पहले की नई लाइन के कारण है, जिसे ommitted नहीं किया जा सकता है। POSIX कोड, जो पुराने संकेतन की तरह अधिक व्यवहार करता है, इस तरह दिखता है:

while read a b c; do
    case $a in ("") break; esac
    # something
done << EOF
$(some_command)
EOF

ज्यादातर मामलों में यह काफी अच्छा होना चाहिए। लेकिन दुर्भाग्य से यह अभी भी पुराने संकेतन की तरह व्यवहार नहीं करता है अगर some_command एक खाली रेखा प्रिंट करता है। पुरानी संकेतन में जबकि शरीर निष्पादित होता है और POSIX संकेतन में हम शरीर के सामने टूट जाते हैं।

इसे ठीक करने का एक तरीका इस तरह दिख सकता है:

while read a b c; do
    case $a in ("something_guaranteed_not_to_be_printed_by_some_command") break; esac
    # something
done << EOF
$(some_command)
echo "something_guaranteed_not_to_be_printed_by_some_command"
EOF

5

एक स्मार्ट स्क्रिप्ट जो PIPE और कमांड लाइन तर्कों के डेटा को पढ़ सकती है:

#!/bin/bash
if [[ -p /proc/self/fd/0 ]]
    then
    PIPE=$(cat -)
    echo "PIPE=$PIPE"
fi
echo "ARGS=$@"

आउटपुट:

$ bash test arg1 arg2
ARGS=arg1 arg2

$ echo pipe_data1 | bash test arg1 arg2
PIPE=pipe_data1
ARGS=arg1 arg2

स्पष्टीकरण: जब कोई स्क्रिप्ट पाइप के माध्यम से कोई डेटा प्राप्त करती है, तो स्टड / प्रोक / सेल्फ / एफडी / 0 एक पाइप के लिए एक सिमलिंक होगा।

/proc/self/fd/0 -> pipe:[155938]

यदि नहीं, तो यह वर्तमान टर्मिनल को इंगित करेगा:

/proc/self/fd/0 -> /dev/pts/5

बैश [[ -pविकल्प यह जांच सकता है कि यह एक पाइप है या नहीं।

cat -से पढ़ता है stdin

यदि हम उपयोग cat -करते हैं जब कोई नहीं होता है stdin, तो यह हमेशा के लिए इंतजार करेगा, यही कारण है कि हमने इसे ifशर्त के अंदर रखा है ।


आप इसका उपयोग भी कर सकते हैं /dev/stdinजो एक लिंक है/proc/self/fd/0
Elie G.

3

एक असाइनमेंट में कुछ अभिव्यक्ति को समाहित करना उस तरह का व्यवहार नहीं करता है।

इसके बजाय, कोशिश करें:

test=$(echo "hello world"); echo test=$test

2

निम्नलिखित कोड:

echo "hello world" | ( test=($(< /dev/stdin)); echo test=$test )

भी काम करेगा, लेकिन यह पाइप के बाद एक और नया उप-खोल खोलेगा, जहां

echo "hello world" | { test=($(< /dev/stdin)); echo test=$test; }

नहीं होगा।


मुझे चेपन्नर्स पद्धति का उपयोग करने के लिए नौकरी नियंत्रण को अक्षम करना पड़ा (मैं टर्मिनल से यह कमांड चला रहा था):

set +m;shopt -s lastpipe
echo "hello world" | read test; echo test=$test
echo "hello world" | test="$(</dev/stdin)"; echo test=$test

बैश मैनुअल कहते हैं :

lastpipe

यदि सेट किया गया है, और नौकरी नियंत्रण सक्रिय नहीं है , तो शेल मौजूदा शेल वातावरण में पृष्ठभूमि में निष्पादित नहीं की गई पाइपलाइन की अंतिम कमांड चलाता है।

नोट: नौकरी नियंत्रण एक गैर-इंटरैक्टिव शेल में डिफ़ॉल्ट रूप से बंद हो जाता है और इस प्रकार आपको set +mस्क्रिप्ट के अंदर की आवश्यकता नहीं होती है ।


1

मुझे लगता है कि आप एक शेल स्क्रिप्ट लिखने की कोशिश कर रहे थे जो स्टड से इनपुट ले सकती थी। लेकिन जब आप इसे इनलाइन करने के लिए प्रयास कर रहे हैं, तो आप उस परीक्षण = चर को बनाने की कोशिश में खो गए। मुझे लगता है कि यह इनलाइन करने के लिए बहुत मायने नहीं रखता है, और इसीलिए यह आपकी अपेक्षा के अनुरूप काम नहीं करता है।

मैं कम करने की कोशिश कर रहा था

$( ... | head -n $X | tail -n 1 )

विभिन्न इनपुट से एक विशिष्ट लाइन प्राप्त करने के लिए। इसलिए मैं टाइप कर सकता हूं ...

cat program_file.c | line 34

इसलिए मुझे स्टड से पढ़ने में सक्षम एक छोटे खोल कार्यक्रम की आवश्यकता है। जैसे आप करते हैं।

22:14 ~ $ cat ~/bin/line 
#!/bin/sh

if [ $# -ne 1 ]; then echo enter a line number to display; exit; fi
cat | head -n $1 | tail -n 1
22:16 ~ $ 

तुम वहाँ जाओ।


-1

इस बारे में कैसा है:

echo "hello world" | echo test=$(cat)

मूल रूप से नीचे मेरे जवाब से एक डुबकी।
djanowski

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