बाश शेल में $ {var}, "$ var" और "$ {var}" में क्या अंतर है?


133

क्या शीर्षक कहते हैं: क्या यह एक चर संपुटित का मतलब क्या है {}, ""या "{}? "मैं इस बारे में कोई स्पष्टीकरण ऑनलाइन ढूँढने में सक्षम नहीं किया गया है - मैं प्रतीकों का प्रयोग कर के अलावा उन्हें का उल्लेख करने, नहीं कर पाए हैं जो कुछ भी नहीं देता है।

यहाँ एक उदाहरण है:

declare -a groups

groups+=("CN=exampleexample,OU=exampleexample,OU=exampleexample,DC=example,DC=com")
groups+=("CN=example example,OU=example example,OU=example example,DC=example,DC=com")

यह:

for group in "${groups[@]}"; do
    echo $group
done

इससे बहुत अलग साबित होता है:

for group in $groups; do
    echo $group
done

और इस:

for group in ${groups}; do
    echo $group
done

केवल पहला ही पूरा करता है कि मुझे क्या चाहिए: सरणी में प्रत्येक तत्व के माध्यम से पुनरावृति करने के लिए। मैं के बीच मतभेद पर वास्तव में स्पष्ट नहीं कर रहा हूँ $groups, "$groups", ${groups}और "${groups}"। अगर कोई इसे समझा सकता है, तो मैं इसकी सराहना करूंगा।

एक अतिरिक्त प्रश्न के रूप में - क्या कोई इन इनकैप्सुलेशन को संदर्भित करने का स्वीकृत तरीका जानता है?


जवाबों:


228

ब्रेसिज़ ( $varबनाम ${var})

ज्यादातर मामलों में, $varऔर ${var}समान हैं:

var=foo
echo $var
# foo
echo ${var}
# foo

ब्रेस को केवल अभिव्यक्ति में अस्पष्टता को हल करने की आवश्यकता होती है:

var=foo
echo $varbar
# Prints nothing because there is no variable 'varbar'
echo ${var}bar
# foobar

उद्धरण ( $varबनाम "$var"बनाम "${var}")

जब आप एक चर के चारों ओर दोहरे उद्धरण जोड़ते हैं, तो आप शेल को एक ही शब्द के रूप में मानते हैं, भले ही उसमें व्हाट्सएप शामिल हो:

var="foo bar"
for i in "$var"; do # Expands to 'for i in "foo bar"; do...'
    echo $i         #   so only runs the loop once
done
# foo bar

निम्नलिखित के साथ उस व्यवहार का विरोध करें:

var="foo bar"
for i in $var; do # Expands to 'for i in foo bar; do...'
    echo $i       #   so runs the loop twice, once for each argument
done
# foo
# bar

$varबनाम के रूप में ${var}, ब्रेसिज़ केवल असंतोष के लिए आवश्यक हैं, उदाहरण के लिए:

var="foo bar"
for i in "$varbar"; do # Expands to 'for i in ""; do...' since there is no
    echo $i            #   variable named 'varbar', so loop runs once and
done                   #   prints nothing (actually "")

var="foo bar"
for i in "${var}bar"; do # Expands to 'for i in "foo barbar"; do...'
    echo $i              #   so runs the loop once
done
# foo barbar

ध्यान दें कि "${var}bar"ऊपर के दूसरे उदाहरण में भी लिखा जा सकता है "${var}"bar, जिस स्थिति में आपको ब्रेस की आवश्यकता नहीं है, अर्थात "$var"bar। हालांकि, यदि आपके स्ट्रिंग में बहुत सारे उद्धरण हैं, तो इन वैकल्पिक रूपों को पढ़ना मुश्किल हो सकता है (और इसलिए बनाए रखना मुश्किल है)। यह पृष्ठ बैश में उद्धृत करने के लिए एक अच्छा परिचय प्रदान करता है।

Arrays ( $varबनाम $var[@]बनाम ${var[@]})

अब आपकी सरणी के लिए। बाश मैनुअल के अनुसार :

बिना सबस्क्रिप्ट के एक ऐरे वेरिएबल को रेफर करना 0 के सबस्क्रिप्ट के साथ एरे को संदर्भित करने के बराबर है।

दूसरे शब्दों में, यदि आप एक सूचकांक की आपूर्ति नहीं करते हैं, तो आपको []सरणी का पहला तत्व मिलता है:

foo=(a b c)
echo $foo
# a

जो बिल्कुल वैसा ही है

foo=(a b c)
echo ${foo}
# a

किसी सरणी के सभी तत्वों को प्राप्त करने के लिए, आपको @सूचकांक के रूप में उपयोग करने की आवश्यकता है , जैसे ${foo[@]}। ब्रेसिज़ को सरणियों के साथ आवश्यक है क्योंकि उनके बिना, शेल $fooपहले भाग का विस्तार करेगा , सरणी का पहला तत्व देगा जिसके बाद एक लीटर होगा [@]:

foo=(a b c)
echo ${foo[@]}
# a b c
echo $foo[@]
# a[@]

यह पृष्ठ बैश में सरणियों का एक अच्छा परिचय है।

पुनरीक्षित उद्धरण ( ${foo[@]}बनाम "${foo[@]}")

आपने इस बारे में नहीं पूछा, लेकिन यह एक सूक्ष्म अंतर है जिसके बारे में जानना अच्छा है। यदि आपके एरे में तत्वों में व्हॉट्सएप हो सकता है, तो आपको दोहरे उद्धरण चिह्नों का उपयोग करने की आवश्यकता है ताकि प्रत्येक तत्व को एक अलग शब्द के रूप में माना जाए: "

foo=("the first" "the second")
for i in "${foo[@]}"; do # Expands to 'for i in "the first" "the second"; do...'
    echo $i              #   so the loop runs twice
done
# the first
# the second

दोहरे उद्धरणों के बिना व्यवहार के साथ इसका विरोध करें:

foo=("the first" "the second")
for i in ${foo[@]}; do # Expands to 'for i in the first the second; do...'
    echo $i            #   so the loop runs four times!
done
# the
# first
# the
# second

3
एक और मामला है: ${var:?}जो चर नहीं होने या सेट नहीं होने पर त्रुटि प्रदान करेगा। REF: github.com/koalaman/shellcheck/wiki/SC2154
Nam Nguyen

4
@NamNguyen आप के अन्य रूपों के बारे में बात करना चाहते हैं पैरामीटर विस्तार : कम से कम एक दर्जन से देखते हैं ${parameter:-word}, ${parameter:=word}, ${parameter#word}, ${parameter/pattern/string}, और इतने पर। मुझे लगता है कि वे इस उत्तर के दायरे से परे हैं।
ThisSuitIsBlackNot

दरअसल, दोहरे उद्धरण चिह्नों की चर्चा अधूरी है। आगे देखें stackoverflow.com/questions/10067266/…
tripleee

11

टी एल; डॉ

आपके द्वारा दिए गए सभी उदाहरण बैश शेल विस्तार पर भिन्नताएं हैं । विस्तार एक विशेष क्रम में होता है, और कुछ में विशिष्ट उपयोग के मामले होते हैं।

टोकन डेलिमिटर के रूप में ब्रेसिज़

${var}वाक्य रचना मुख्य रूप से अस्पष्ट टोकन परिसीमन के लिए प्रयोग किया जाता है। उदाहरण के लिए, निम्नलिखित पर विचार करें:

$ var1=foo; var2=bar; var12=12
$ echo $var12
12
$ echo ${var1}2
foo2

ऐरे विस्तार में ब्रेसिज़

एक सरणी के तत्वों और अन्य विशेष विस्तार के लिए ब्रेसिज़ की आवश्यकता होती है । उदाहरण के लिए:

$ foo=(1 2 3)

# Returns first element only.
$ echo $foo
1

# Returns all array elements.
$ echo ${foo[*]}
1 2 3

# Returns number of elements in array.
$ echo ${#foo[*]}
3

tokenization

आपके अधिकांश सवालों के उद्धरण के साथ क्या करना है, और शेल इनपुट को कैसे बताता है। इस अंतर पर विचार करें कि शेल निम्नलिखित उदाहरणों में शब्द विभाजन कैसे करता है :

$ var1=foo; var2=bar; count_params () { echo $#; }

# Variables are interpolated into a single string.
$ count_params "$var1 $var2"
1

# Each variable is quoted separately, created two arguments.
$ count_params "$var1" "$var2"
2

@प्रतीक की तुलना में अलग हवाले से साथ सूचना का आदान *। विशेष रूप से:

  1. $@ "[ई] एक से शुरू होने वाली स्थितिगत मापदंडों के लिए xpands। जब विस्तार दोहरे उद्धरण चिह्नों के भीतर होता है, तो प्रत्येक पैरामीटर एक अलग शब्द में फैलता है।"
  2. एक सरणी में, "[i] f शब्द दोहरा-उद्धृत है, ${name[*]}IFS चर के पहले वर्ण द्वारा अलग किए गए प्रत्येक सरणी सदस्य के मान के साथ एक शब्द में ${name[@]}विस्तारित होता है , और नाम के प्रत्येक तत्व को एक अलग शब्द में विस्तारित करता है।"

आप इसे इस प्रकार देख सकते हैं:

$ count_params () { echo $#; }
$ set -- foo bar baz 

$ count_params "$@"
3

$ count_params "$*"
1

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


7

आपको सरणियों और सरल चर के बीच अंतर करने की आवश्यकता है - और आपका उदाहरण एक सरणी का उपयोग कर रहा है।

सादे चर के लिए:

  • $varऔर ${var}बिल्कुल बराबर हैं।
  • "$var"और "${var}"बिल्कुल बराबर हैं।

हालांकि, दोनों जोड़े सभी मामलों में 100% समान नहीं हैं। नीचे उत्पादन पर विचार करें:

$ var="  abc  def  "
$ printf "X%sX\n" $var
XabcX
XdefX
$ printf "X%sX\n" "${var}"
X  abc  def  X
$

चर के आसपास दोहरे उद्धरण चिह्नों के बिना, आंतरिक रिक्ति खो जाती है और विस्तार को printfकमांड के दो तर्कों के रूप में माना जाता है । चर के आसपास दोहरे उद्धरण चिह्नों के साथ, आंतरिक रिक्ति को संरक्षित किया जाता है और विस्तार को printfकमांड के एक तर्क के रूप में माना जाता है ।

सरणियों के साथ, नियम समान और भिन्न दोनों हैं।

  • यदि groupsकोई सरणी है, तो संदर्भित करना $groupsया ${groups}संदर्भित करने के ${groups[0]}लिए टैंनामाउंट है, सरणी का शून्य तत्व।
  • सन्दर्भ संदर्भित करने के "${groups[@]}"लिए अनुरूप है "$@"; यह सरणी के अलग-अलग तत्वों में रिक्ति को संरक्षित करता है, और मानों की सूची देता है, एरे के तत्व के प्रति एक मान।
  • ${groups[@]}दोहरे उद्धरण चिह्नों के बिना संदर्भित करने से रिक्ति का संरक्षण नहीं होता है और सरणी में तत्व होने की तुलना में अधिक मूल्यों का परिचय दे सकता है यदि कुछ तत्वों में रिक्त स्थान होते हैं।

उदाहरण के लिए:

$ groups=("abc def" "  pqr  xyz  ")
$ printf "X%sX\n" ${groups[@]}
XabcX
XdefX
XpqrX
XxyzX
$ printf "X%sX\n" "${groups[@]}"
Xabc defX
X  pqr  xyz  X
$ printf "X%sX\n" $groups
XabcX
XdefX
$ printf "X%sX\n" "$groups"
Xabc defX
$

सुराग के *बजाय का उपयोग करके @अलग-अलग परिणाम प्राप्त करना।

एक bashस्क्रिप्ट में तर्कों पर पुनरावृति कैसे करें, यह भी देखें ।


3

के तहत पहले पैराग्राफ के दूसरे वाक्य पैरामीटर विस्तार में man bashकहते हैं,

विस्तार किए जाने वाले पैरामीटर नाम या प्रतीक को ब्रेसिज़ में संलग्न किया जा सकता है, जो वैकल्पिक हैं, लेकिन चर को वर्णों से विस्तारित करने के लिए इसे बाद में संरक्षित किया जा सकता है, जिसे नाम के भाग के रूप में व्याख्या किया जा सकता है।

जो आपको बताता है कि नाम केवल ब्रेसिज़ है , और मुख्य उद्देश्य स्पष्ट करना है कि नाम कहाँ से शुरू और समाप्त होता है:

foo='bar'
echo "$foobar"
# nothing
echo "${foo}bar"
barbar

यदि आप आगे पढ़ते हैं तो आपको पता चलता है,

जब पैरामीटर एक से अधिक अंकों के साथ स्थितीय पैरामीटर होता है, तो ब्रेस की आवश्यकता होती है ...

चलो परीक्षण:

$ set -- {0..100}
$ echo $22
12
$ echo ${22}
20

हुह। साफ। मैं ईमानदारी से नहीं जानता कि यह लिखने से पहले (मेरे पास पहले से अधिक 9 स्थितियां कभी नहीं थीं।)

बेशक, आपको शक्तिशाली पैरामीटर विस्तार सुविधाओं जैसे करने के लिए ब्रेसिज़ की भी आवश्यकता होती है

${parameter:-word}
${parameter:=word}
${parameter:?word}
 [read the section for more]

साथ ही सरणी विस्तार।


3

एक संबंधित मामला ऊपर कवर नहीं किया गया। एक खाली चर का हवाला देते हुए चीजों को बदलने के लिए लगता है test -n। यह विशेष रूप से के लिए infoपाठ में एक उदाहरण के रूप में दिया गया है coreutils, लेकिन वास्तव में समझाया नहीं गया है:

16.3.4 String tests
-------------------

These options test string characteristics.  You may need to quote
STRING arguments for the shell.  For example:

     test -n "$V"

  The quotes here prevent the wrong arguments from being passed to
`test' if `$V' is empty or contains special characters.

मैं विस्तृत विवरण सुनना पसंद करूंगा। मेरा परीक्षण इसकी पुष्टि करता है, और मैं अब सभी स्ट्रिंग परीक्षणों के लिए अपने चर को उद्धृत कर रहा हूं, ताकि एक ही परिणाम होने से बचने -zऔर -nवापस करने के लिए।

$ unset a
$ if [ -z $a ]; then echo unset; else echo set; fi
unset
$ if [ -n $a ]; then echo set; else echo unset; fi    
set                                                   # highly unexpected!

$ unset a
$ if [ -z "$a" ]; then echo unset; else echo set; fi
unset
$ if [ -n "$a" ]; then echo set; else echo unset; fi
unset                                                 # much better

2

खैर, मुझे पता है कि एक चर का एनकैप्सुलेशन आपको कुछ इस तरह से काम करने में मदद करता है:

${groups%example}

या उस तरह के वाक्यविन्यास, जहां आप मूल्य को वापस करने से पहले अपने चर के साथ कुछ करना चाहते हैं।

अब, यदि आप अपना कोड देखते हैं, तो सभी जादू अंदर है

${groups[@]}

जादू वहाँ है क्योंकि आप बस नहीं लिख सकते हैं: $groups[@]

आप अपने चर को अंदर रख रहे हैं {}क्योंकि आप विशेष वर्णों का उपयोग करना चाहते हैं []और @। आप अपने वैरिएबल को केवल नाम या कॉल नहीं कर सकते: @या something[]क्योंकि ये अन्य संचालन और नामों के लिए आरक्षित वर्ण हैं।


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