बाश में एक कमांड में कमांड का आउटपुट पढ़ना


111

मुझे अपनी स्क्रिप्ट में एक कमांड के आउटपुट को एक सरणी में पढ़ना होगा। उदाहरण के लिए कमांड है:

ps aux | grep | grep | x 

और यह इस तरह से लाइन द्वारा आउटपुट लाइन देता है:

10
20
30

मुझे कमांड आउटपुट से सरणी में मान पढ़ने की आवश्यकता है, और फिर सरणी का आकार तीन से कम होने पर मैं कुछ काम करूंगा।


5
अरे @barp, आपके सवाल का जवाब दें, ऐसा न हो कि आपके प्रकार पूरे समुदाय पर एक नाली हो।
जेम्स

9
@ इस मुद्दे पर इस तथ्य के साथ नहीं कि वह अपने प्रश्न का उत्तर नहीं दे रहा है ... यह एक क्यू / ए साइट है। उन्होंने सिर्फ उन्हें जवाब के रूप में चिह्नित नहीं किया । उसे चिह्नित करना चाहिए। संकेत। @ barp
DDPWNAGE

4
कृपया @barp, प्रश्न के उत्तर के रूप में चिह्नित करें।
स्मोनफ

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

जवाबों:


161

अन्य उत्तर (जो बल्कि अक्सर होता है) टूट जाएगा अगर आदेश के उत्पादन में रिक्त स्थान शामिल है या जैसे पात्रों glob *, ?, [...]

सरणी में कमांड का आउटपुट प्राप्त करने के लिए, प्रति तत्व एक लाइन के साथ, अनिवार्य रूप से 3 तरीके हैं:

  1. Bash :4 उपयोग के साथ- mapfileयह सबसे कुशल है:

    mapfile -t my_array < <( my_command )
  2. अन्यथा, उत्पादन को पढ़ने वाला एक लूप (धीमा, लेकिन सुरक्षित):

    my_array=()
    while IFS= read -r line; do
        my_array+=( "$line" )
    done < <( my_command )
  3. जैसा कि चार्ल्स डफी ने टिप्पणियों में दिया है (धन्यवाद!), निम्नलिखित संख्या 2 में लूप विधि से बेहतर प्रदर्शन कर सकता है:

    IFS=$'\n' read -r -d '' -a my_array < <( my_command && printf '\0' )

    कृपया सुनिश्चित करें कि आप वास्तव में इस फ़ॉर्म का उपयोग करते हैं, अर्थात, सुनिश्चित करें कि आपके पास निम्नलिखित हैं:

    • IFS=$'\n' readबयान के रूप में एक ही पंक्ति पर : यह केवल केवल कथन के IFS लिएread पर्यावरण चर सेट करेगा तो यह आपकी स्क्रिप्ट के बाकी हिस्सों को बिल्कुल भी प्रभावित नहीं करेगा। इस चर का उद्देश्य readईओएल चरित्र पर धारा को तोड़ने के लिए बताना है \n
    • -r: यह महत्वपूर्ण है। यह बताता read है कि बैकस्लैश की व्याख्या सीक्वेंस से बचकर नहीं होती है।
    • -d '': कृपया -dविकल्प और उसके तर्क के बीच के स्थान पर ध्यान दें ''। यदि आप यहां स्थान नहीं छोड़ते हैं, तो ''कभी नहीं देखा जाएगा, क्योंकि यह बैश हटाने के चरण में गायब हो जाएगा जब बैश ने बयान दिया। यह readनील बाइट पर पढ़ना बंद करने के लिए कहता है। कुछ लोग इसे लिखते हैं -d $'\0', लेकिन यह वास्तव में आवश्यक नहीं है। -d ''बेहतर है।
    • -a my_arrayस्ट्रीम को पढ़ते हुए readसरणी को पॉप्युलेट करना बताता है my_array
    • आपको printf '\0'विवरण के बाद का उपयोग करना चाहिए my_command, ताकि readरिटर्न 0; यह वास्तव में एक बड़ी बात नहीं है यदि आप नहीं करते हैं (आपको बस एक रिटर्न कोड मिलेगा 1, जो ठीक है यदि आप उपयोग नहीं करते हैं set -e- जो आपको वैसे भी नहीं करना चाहिए), लेकिन बस इसे ध्यान में रखें। यह क्लीनर और अधिक शब्दार्थिक रूप से सही है। ध्यान दें कि यह अलग है printf '', जो कुछ भी आउटपुट नहीं करता है। ख़ुशी से वहाँ पढ़ने से रोकने के लिए ( विकल्प याद है ?) की printf '\0'जरूरत है, एक शून्य बाइट प्रिंट readकरता है -d ''

यदि आप कर सकते हैं, यानी, यदि आप सुनिश्चित हैं कि आपका कोड Bash ,4 पर चलेगा, तो पहली विधि का उपयोग करें। और आप देख सकते हैं कि यह छोटा है।

यदि आप उपयोग करना चाहते हैं read, तो लूप (विधि 2) विधि 3 पर एक फायदा हो सकता है यदि आप कुछ प्रसंस्करण करना चाहते हैं जैसा कि लाइनें पढ़ी जाती हैं: आपके पास इसकी सीधी पहुंच है ( $lineउदाहरण में दिए गए चर के माध्यम से ), और आपके पास पहले से पढ़ी गई पंक्तियों तक भी पहुंच है ( ${my_array[@]}उदाहरण के अनुसार मैंने जो उदाहरण दिया है)।

ध्यान दें कि mapfileपढ़ी गई प्रत्येक पंक्ति पर कॉलबैक निकालने का एक तरीका प्रदान करता है, और वास्तव में आप इसे केवल इस कॉलबैक को कॉल करने के लिए भी कह सकते हैं जो पढ़ी गई प्रत्येक N लाइनें हैं; एक नज़र help mapfileऔर विकल्प -Cऔर -cउसमें। (इसके बारे में मेरी राय यह है कि यह थोड़ा क्लिंक है, लेकिन कभी-कभी इसका इस्तेमाल किया जा सकता है यदि आपके पास केवल करने के लिए सरल चीजें हैं - मुझे वास्तव में समझ में नहीं आता है कि यह पहले स्थान पर भी क्यों लागू किया गया था!)।


अब मैं आपको बताने जा रहा हूँ कि निम्न विधि क्यों:

my_array=( $( my_command) )

रिक्त स्थान होने पर टूट जाता है:

$ # I'm using this command to test:
$ echo "one two"; echo "three four"
one two
three four
$ # Now I'm going to use the broken method:
$ my_array=( $( echo "one two"; echo "three four" ) )
$ declare -p my_array
declare -a my_array='([0]="one" [1]="two" [2]="three" [3]="four")'
$ # As you can see, the fields are not the lines
$
$ # Now look at the correct method:
$ mapfile -t my_array < <(echo "one two"; echo "three four")
$ declare -p my_array
declare -a my_array='([0]="one two" [1]="three four")'
$ # Good!

फिर कुछ लोग IFS=$'\n'इसे ठीक करने के लिए उपयोग करने की सलाह देंगे :

$ IFS=$'\n'
$ my_array=( $(echo "one two"; echo "three four") )
$ declare -p my_array
declare -a my_array='([0]="one two" [1]="three four")'
$ # It works!

लेकिन अब ग्लब्स के साथ एक और कमांड का उपयोग करते हैं :

$ echo "* one two"; echo "[three four]"
* one two
[three four]
$ IFS=$'\n'
$ my_array=( $(echo "* one two"; echo "[three four]") )
$ declare -p my_array
declare -a my_array='([0]="* one two" [1]="t")'
$ # What?

ऐसा इसलिए है क्योंकि मेरे पास tमौजूदा निर्देशिका में एक फ़ाइल है ... और यह फ़ाइल नाम ग्लोब से मेल खाता है [three four]... इस बिंदु पर कुछ लोग set -fग्लोबिंग को अक्षम करने के लिए उपयोग करने की सलाह देंगे : लेकिन इसे देखें: आपको बदलना होगा IFSऔर set -fएक को ठीक करने में सक्षम होना चाहिए टूटी हुई तकनीक (और आप इसे वास्तव में ठीक भी नहीं कर रहे हैं)! ऐसा करते समय हम वास्तव में शेल के खिलाफ लड़ रहे हैं , शेल के साथ काम नहीं कर रहे हैं

$ mapfile -t my_array < <( echo "* one two"; echo "[three four]")
$ declare -p my_array
declare -a my_array='([0]="* one two" [1]="[three four]")'

यहाँ हम शेल के साथ काम कर रहे हैं!


4
यह बहुत अच्छा है, मैंने इसके बारे में mapfileपहले कभी नहीं सुना , यह वही है जो मैं वर्षों से याद कर रहा हूं। मुझे लगता है कि हाल ही के संस्करणों bashमें इतनी अच्छी नई विशेषताएं हैं, मुझे केवल कुछ दिन डॉक्स पढ़ने और एक अच्छा धोखा देने के लिए लिखना चाहिए।
जीन पावलोवस्की

6
Btw, < <(command)शेल स्क्रिप्ट्स में इस सिंटैक्स का उपयोग करने के लिए , शेलबैंग लाइन होनी चाहिए #!/bin/bash- अगर रन के रूप में #!/bin/sh, बैश एक सिंटैक्स त्रुटि के साथ बाहर निकल जाएगा।
जीन पावलोवस्की

1
@ GenePavlovsky के सहायक नोट पर विस्तार करते हुए, स्क्रिप्ट को बैश कमांड के साथ भी चलाया जाना चाहिए bash my_script.shन कि sh कमांडsh my_script.sh
Vito

2
@ वीटो: वास्तव में, यह उत्तर केवल बैश के लिए है, लेकिन यह एक समस्या नहीं होनी चाहिए, क्योंकि कड़ाई से अनुपालन करने वाले POSIX गोले भी सरणियों को लागू नहीं करते हैं ( shऔर dashसरणियों के बारे में बिल्कुल भी नहीं जानते हैं, सिवाय इसके, के लिए, स्थितीय पैरामीटर $@सरणी)।
gniourf_gniourf

3
एक अन्य विकल्प के लिए जिसे bash 4.0 की आवश्यकता नहीं है, पर विचार करें IFS=$'\n' read -r -d '' -a my_array < <(my_command && printf '\0')- यह दोनों bash 3.x में सही ढंग से काम करता है, और से एक विफल निकास स्थिति से भी गुजरता my_commandहै read
चार्ल्स डफी

86

आप उपयोग कर सकते हैं

my_array=( $(<command>) )

<command>सरणी में कमांड का आउटपुट स्टोर करने के लिए my_array

आप उस सरणी की लंबाई तक पहुंच का उपयोग कर सकते हैं

my_array_length=${#my_array[@]}

अब लंबाई में संग्रहीत किया जाता है my_array_length


19
क्या होगा यदि $ (कमांड) के आउटपुट में रिक्त स्थान और कई लाइनें हैं? मैंने "$ (कमांड)" जोड़ा और यह सभी लाइनों से सभी आउटपुट को पहले [0] एरे के तत्व में रखता है।
ikwyl6

3
@ ikwyl6 एक वर्कअराउंड कमांड आउटपुट को एक चर में असाइन कर रहा है और फिर इसके साथ एक सरणी बना रहा है या इसे एक सरणी में जोड़ रहा है। VAR="$(<command>)"और फिर my_array=("$VAR")याmy_array+=("$VAR")
वीटो

10

कल्पना करें कि आप फ़ाइलों और निर्देशिका नामों (वर्तमान फ़ोल्डर के तहत) को एक सरणी में रखने जा रहे हैं और इसके आइटमों की गिनती कर रहे हैं। स्क्रिप्ट की तरह होगा;

my_array=( `ls` )
my_array_length=${#my_array[@]}
echo $my_array_length

या, आप निम्न स्क्रिप्ट जोड़कर इस सरणी पर पुनरावृति कर सकते हैं:

for element in "${my_array[@]}"
do
   echo "${element}"
done

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


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