अन्य उत्तर (जो बल्कि अक्सर होता है) टूट जाएगा अगर आदेश के उत्पादन में रिक्त स्थान शामिल है या जैसे पात्रों glob *, ?, [...]।
सरणी में कमांड का आउटपुट प्राप्त करने के लिए, प्रति तत्व एक लाइन के साथ, अनिवार्य रूप से 3 तरीके हैं:
Bash :4 उपयोग के साथ- mapfileयह सबसे कुशल है:
mapfile -t my_array < <( my_command )
अन्यथा, उत्पादन को पढ़ने वाला एक लूप (धीमा, लेकिन सुरक्षित):
my_array=()
while IFS= read -r line; do
my_array+=( "$line" )
done < <( my_command )
जैसा कि चार्ल्स डफी ने टिप्पणियों में दिया है (धन्यवाद!), निम्नलिखित संख्या 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]")'
यहाँ हम शेल के साथ काम कर रहे हैं!