बाश पूरा होने के संदर्भ में $ {सरणी [*]} बनाम $ {सरणी [@]} के बारे में एक भ्रम


89

मैं पहली बार एक बैश पूरा लिखने पर एक छुरा ले जा रहा हूं, और मैं बैर सरणियों ( ${array[@]}और ${array[*]}) के दो तरीकों के बारे में थोड़ा भ्रमित हूं ।

यहां कोड का प्रासंगिक हिस्सा है (यह काम करता है, वैसे, लेकिन मैं इसे बेहतर समझना चाहूंगा):

_switch()
{
    local cur perls
    local ROOT=${PERLBREW_ROOT:-$HOME/perl5/perlbrew}
    COMPREPLY=()
    cur=${COMP_WORDS[COMP_CWORD]}
    perls=($ROOT/perls/perl-*)
    # remove all but the final part of the name
    perls=(${perls[*]##*/})

    COMPREPLY=( $( compgen -W "${perls[*]} /usr/bin/perl" -- ${cur} ) )
}

बैश के दस्तावेज कहते हैं :

किसी सरणी के किसी भी तत्व को $ {name [सबस्क्रिप्ट]} का उपयोग करके संदर्भित किया जा सकता है। ब्रेस को शेल के फाइलनाम विस्तार ऑपरेटरों के साथ संघर्ष से बचने के लिए आवश्यक है। यदि सबस्क्रिप्ट '@' या '*' है, तो शब्द ऐरे नाम के सभी सदस्यों तक फैलता है। ये सदस्यता केवल तभी भिन्न होती है जब शब्द दोहरे उद्धरण चिह्नों में प्रकट होता है। यदि यह शब्द दोहरा-उद्धृत है, तो $ {name [*]} IFS चर के पहले वर्ण द्वारा अलग किए गए प्रत्येक सरणी सदस्य के मान के साथ एक शब्द तक फैलता है, और $ {name [@]} नाम के प्रत्येक तत्व का विस्तार करता है एक अलग शब्द के लिए।

अब मुझे लगता है कि मैं समझता हूं कि compgen -Wएक स्ट्रिंग संभावित विकल्पों की शब्द सूची से युक्त है, लेकिन इस संदर्भ में मुझे समझ में नहीं आता है कि "$ {name [@]} प्रत्येक तत्व को एक अलग शब्द के अर्थ में विस्तारित करता है"।

लंबी कहानी छोटी: ${array[*]}काम करता है; ${array[@]}ऐसा नहीं करता। मैं जानना चाहूंगा कि क्यों, और मैं बेहतर तरीके से समझना चाहता हूं कि वास्तव में इसका ${array[@]}विस्तार क्या है ।

जवाबों:


119

(यह कालेब पेडर्सन के जवाब पर मेरी टिप्पणी का एक विस्तार है - कि [@]बनाम के अधिक सामान्य उपचार के लिए उस उत्तर को देखें [*])।

जब बैश (या कोई समान शेल) एक कमांड लाइन को पार्स करता है, तो इसे "शब्दों" की श्रृंखला में विभाजित करता है (जिसे मैं बाद में भ्रम से बचने के लिए "शेल-शब्द" कहूंगा)। आम तौर पर, शेल-शब्द रिक्त स्थान (या अन्य व्हाट्सएप) द्वारा अलग किए जाते हैं, लेकिन रिक्त स्थान को शेल-वर्ड में उन्हें छोड़कर या उद्धृत करके शामिल किया जा सकता है। डबल-कोट्स में [@]and [*]-andanded सरणियों के बीच का अंतर यह है कि "${myarray[@]}"सरणी के प्रत्येक तत्व को एक अलग शेल-शब्द के रूप में माना जाता है, जबकि "${myarray[*]}"रिक्त स्थान द्वारा अलग किए गए सरणी के सभी तत्वों के साथ एकल शेल-शब्द में परिणाम होता है (या जो कुछ भी पहला चरित्र IFSहै)।

आमतौर पर, [@]व्यवहार वही होता है जो आप चाहते हैं। मान लीजिए कि हमारे पास perls=(perl-one perl-two)और उपयोग है ls "${perls[*]}"- जो इसके बराबर है ls "perl-one perl-two", जो नाम की एकल फ़ाइल की तलाश करेगा perl-one perl-two, जो कि शायद आप क्या चाहते थे। ls "${perls[@]}"के बराबर है ls "perl-one" "perl-two", जो कुछ उपयोगी करने की अधिक संभावना है।

पूरा होने वाले शब्दों की एक सूची प्रदान करना (जिसे मैं शेल-शब्दों के साथ भ्रम से बचने के लिए कंप-शब्द कहूंगा) compgenअलग है; -Wविकल्प कंप्यूटर अनुप्रयोग-शब्दों की एक सूची लेता है, लेकिन यह रिक्तियों से अलग कंप्यूटर अनुप्रयोग-शब्दों के साथ एक भी खोल-शब्द के रूप में होना चाहिए। ध्यान दें कि कमांड विकल्प जो हमेशा तर्कों को लेते हैं (कम से कम जहाँ तक मुझे पता है) एक एकल शेल-शब्द लेते हैं - अन्यथा विकल्प के तर्कों और नियमित कमांड तर्कों (/ अन्य) को बताने का कोई तरीका नहीं होगा। विकल्प झंडे) शुरू।

विस्तृत रूप में:

perls=(perl-one perl-two)
compgen -W "${perls[*]} /usr/bin/perl" -- ${cur}

के बराबर है:

compgen -W "perl-one perl-two /usr/bin/perl" -- ${cur}

... जो चाहता है वही करता है। दूसरी ओर,

perls=(perl-one perl-two)
compgen -W "${perls[@]} /usr/bin/perl" -- ${cur}

के बराबर है:

compgen -W "perl-one" "perl-two /usr/bin/perl" -- ${cur}

... जो पूर्ण बकवास है: "पर्ल-वन" एकमात्र एकमात्र शब्द है जो डब्ल्यू-फ्लैग से जुड़ा हुआ है, और पहला वास्तविक तर्क - जो कॉम्पैन को पूरा होने के लिए स्ट्रिंग के रूप में ले जाएगा - है "पर्ल-टू / usr / bin / पर्ल "। मैं शिकायत करने की उम्मीद करता हूं कि इसे अतिरिक्त तर्क दिए गए हैं ("-" और जो कुछ भी $ कर्व में है), लेकिन जाहिर है कि यह सिर्फ उन्हें अनदेखा करता है।


3
यह उत्कृष्ट है; धन्यवाद। मैं वास्तव में चाहता हूं कि यह और जोर से फूंके, लेकिन यह कम से कम स्पष्ट करता है कि यह काम क्यों नहीं किया।
टेलीमेकस

60

आपका शीर्षक ${array[@]}बनाम के बारे में पूछता है ${array[*]}लेकिन फिर आप $array[*]बनाम के बारे में पूछते हैं $array[@]जो थोड़ा भ्रमित करने वाला है। मैं दोनों का जवाब दूंगा:

जब आप किसी ऐरे वेरिएबल को कोट करते हैं और @सबस्क्रिप्ट के रूप में उपयोग करते हैं , तो एरे के प्रत्येक तत्व को व्हाट्सएप (वास्तव में, उनमें से एक $IFS) की पूरी सामग्री तक विस्तारित किया जाता है, जो उस सामग्री के भीतर मौजूद हो सकता है। जब आप तारांकन ( *) को सबस्क्रिप्ट के रूप में उपयोग करते हैं (भले ही यह उद्धृत हो या न हो) तो यह प्रत्येक ऐरे तत्व की सामग्री को तोड़कर बनाई गई नई सामग्री तक विस्तारित हो सकती है $IFS

यहाँ उदाहरण स्क्रिप्ट है:

#!/bin/sh

myarray[0]="one"
myarray[1]="two"
myarray[3]="three four"

echo "with quotes around myarray[*]"
for x in "${myarray[*]}"; do
        echo "ARG[*]: '$x'"
done

echo "with quotes around myarray[@]"
for x in "${myarray[@]}"; do
        echo "ARG[@]: '$x'"
done

echo "without quotes around myarray[*]"
for x in ${myarray[*]}; do
        echo "ARG[*]: '$x'"
done

echo "without quotes around myarray[@]"
for x in ${myarray[@]}; do
        echo "ARG[@]: '$x'"
done

और यहाँ इसका आउटपुट है:

with quotes around myarray[*]
ARG[*]: 'one two three four'
with quotes around myarray[@]
ARG[@]: 'one'
ARG[@]: 'two'
ARG[@]: 'three four'
without quotes around myarray[*]
ARG[*]: 'one'
ARG[*]: 'two'
ARG[*]: 'three'
ARG[*]: 'four'
without quotes around myarray[@]
ARG[@]: 'one'
ARG[@]: 'two'
ARG[@]: 'three'
ARG[@]: 'four'

मैं व्यक्तिगत रूप से आमतौर पर चाहता हूं "${myarray[@]}"। अब, आपके प्रश्न के दूसरे भाग का उत्तर देने के लिए, ${array[@]}बनाम $array[@]

बैश डॉक्स को उद्धृत करते हुए, जिसे आपने उद्धृत किया है:

ब्रेस को शेल के फाइलनाम विस्तार ऑपरेटरों के साथ संघर्ष से बचने के लिए आवश्यक है।

$ myarray=
$ myarray[0]="one"
$ myarray[1]="two"
$ echo ${myarray[@]}
one two

लेकिन, जब आप करते हैं $myarray[@], तो डॉलर का संकेत बहुत हद तक बाध्य होता है myarrayइसलिए इसका मूल्यांकन पहले किया जाता है [@]। उदाहरण के लिए:

$ ls $myarray[@]
ls: cannot access one[@]: No such file or directory

लेकिन, जैसा कि प्रलेखन में उल्लेख किया गया है, कोष्ठक फ़ाइल नाम के विस्तार के लिए हैं, तो चलिए यह कोशिश करते हैं:

$ touch one@
$ ls $myarray[@]
one@

अब हम देख सकते हैं कि फ़ाइल नाम का विस्तार विस्तार के बाद हुआ $myarray

और एक और नोट, $myarrayबिना सबस्क्रिप्ट के सरणी के पहले मूल्य तक फैलता है:

$ myarray[0]="one four"
$ echo $myarray[5]
one four[5]

1
यह भी देखें इस के बारे में कैसे IFSउत्पादन अलग तरह से प्रभावित करता है पर निर्भर करता है @बनाम *और बनाम गैर उद्धृत उद्धृत।
अगली सूचना तक रोक दिया गया।

मैं माफी चाहता हूं, क्योंकि यह इस संदर्भ में काफी महत्वपूर्ण है, लेकिन मैं हमेशा मतलब था ${array[*]}या ${array[@]}। ब्रेसिज़ की कमी बस लापरवाही थी। इसके अलावा, क्या आप समझा सकते हैं ${array[*]}कि compgenकमांड में क्या विस्तार होगा ? यही है, उस संदर्भ में अपने प्रत्येक तत्व में सरणी को अलग से विस्तारित करने का क्या मतलब है?
टेलीमेकस

इसे दूसरे तरीके से रखने के लिए, आप (लगभग हर स्रोत की तरह) कहते हैं कि ${array[@]}आमतौर पर जाने का रास्ता है। क्या मैं समझने की कोशिश कर रहा हूँ क्यों में यह मामला है केवल ${array[*]} काम करता है।
टेलीमेकस

1
ऐसा इसलिए है क्योंकि -W विकल्प के साथ आपूर्ति की गई शब्द सूची को एक शब्द के रूप में दिया जाना चाहिए (जो तब आईएफएस पर आधारित विभाजन होता है)। यदि इसे कॉम्पजेन (जो [@] करता है) को सौंपने से पहले अलग-अलग शब्दों में विभाजित हो जाता है, तो कॉम्पगेन सोचेंगे कि केवल पहला -W साथ आता है, और बाकी नियमित तर्क हैं (और मुझे लगता है कि यह केवल एक तर्क की उम्मीद करता है,) और इसलिए बारफ) होगा।
गॉर्डन डेविसन

@ गॉर्डन: इसे एक उत्तर में ले जाएं, और मैं इसे स्वीकार करूंगा। यही मैं वास्तव में जानना चाहता था। धन्यवाद। (Btw, यह एक स्पष्ट तरीके से बर्फ़ नहीं करता है। यह चुपचाप बार-बार होता है - जो यह जानना मुश्किल बनाता है कि क्या गलत हुआ।)
टेलीमेकस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.