it-swarm.asia

ما الفرق بين $ {var} و "$ var" و "$ {var}" في غلاف Bash؟

ما يقوله العنوان: ماذا يعني تضمين متغير في {} أو "" أو "{} "؟ لم أتمكن من العثور على أي تفسيرات عبر الإنترنت حول هذا - لم أتمكن من الرجوع إليهم باستثناء استخدام الرموز ، والتي لا تسفر عن أي شيء.

إليك مثال:

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}". إذا كان أي شخص يمكن أن يفسر ذلك ، وسأكون ممتنا.

كسؤال إضافي - هل يعرف أي شخص الطريقة المقبولة للإشارة إلى هذه العبوات؟

100
SheerSt

الحمالات ($var vs. ${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 vs. "$var" vs. "${var}")

عندما تضيف علامات اقتباس مزدوجة حول متغير ، فإنك تخبر شركة Shell بمعاملتها على أنها كلمة واحدة ، حتى إذا كانت تحتوي على مسافات بيضاء:

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 vs. ${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. ومع ذلك ، إذا كان لديك الكثير من علامات الاقتباس في السلسلة ، فقد يصعب قراءة هذه النماذج البديلة (وبالتالي يصعب الحفاظ عليها). هذه الصفحة توفر مقدمة جيدة للاقتباس في باش.

المصفوفات ($var vs. $var[@] vs. ${var[@]})

الآن لمجموعة الخاص بك. وفقا ل دليل باش :

الرجوع إلى متغير صفيف بدون اشتراك يكافئ الرجوع إلى الصفيف مع 0.

بمعنى آخر ، إذا لم تقم بتزويد فهرس بـ [] ، فستحصل على العنصر الأول للصفيف:

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

وهو بالضبط نفس الشيء

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

للحصول على جميع عناصر المصفوفة ، تحتاج إلى استخدام @ كالفهرس ، على سبيل المثال ${foo[@]}. الدعامات مطلوبة مع المصفوفات لأنه بدونها ، ستقوم Shell بتوسيع الجزء $foo أولاً ، مع إعطاء العنصر الأول للصفيف متبوعًا بـ [@] الحرفي:

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

هذه الصفحة هي مقدمة جيدة للصفائف في باش.

إعادة النظر في الاقتباسات (${foo[@]} vs. "${foo[@]}")

أنت لم تسأل عن هذا ولكن هذا فارق خفي من الجيد معرفته. إذا كانت العناصر في الصفيف الخاص بك قد تحتوي على مسافة بيضاء ، فأنت بحاجة إلى استخدام علامات اقتباس مزدوجة بحيث يتم التعامل مع كل عنصر على أنه "Word:" منفصل

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
181
ThisSuitIsBlackNot

TL، DR

جميع الأمثلة التي تقدمها هي أشكال مختلفة على Bash Shell Expansions . تحدث التوسعات بترتيب معين ، وبعضها له حالات استخدام محددة.

تستعد كمحددات رمزية

يستخدم بناء الجملة ${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. [email protected] "[e] xpands إلى المعلمات الموضعية ، بدءًا من واحد. عندما يحدث التوسع ضمن علامات اقتباس مزدوجة ، يتم توسيع كل معلمة إلى Word منفصل."
  2. في صفيف ، "[i] إذا كان Word مزدوجًا ، يتم توسيع ${name[*]} إلى Word واحد بقيمة كل عضو صفيف مفصولة بالحرف الأول لمتغير IFS ، و ${name[@]} يوسع كل عنصر من عناصر الاسم إلى Word منفصل ".

يمكنك أن ترى هذا في العمل على النحو التالي:

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

$ count_params "[email protected]"
3

$ count_params "$*"
1

إن استخدام التوسعة المقتبسة أمر مهم للغاية عندما تشير المتغيرات إلى القيم ذات المسافات أو الأحرف الخاصة التي قد تمنع Shell من تقسيم الكلمات بالطريقة التي تنوي تنفيذها. راجع Quoting لمزيد من المعلومات حول كيفية عمل الاقتباس في Bash.

10
Todd A. Jacobs

تحتاج إلى التمييز بين المصفوفات والمتغيرات البسيطة - ومثالك يستخدم صفيفًا.

للمتغيرات البسيطة:

  • $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]} ، عنصر zeroth للصفيف.
  • الرجوع إلى "${groups[@]}" يشبه الرجوع إلى "[email protected]" ؛ يحتفظ التباعد في العناصر الفردية للصفيف ، ويعيد قائمة القيم ، قيمة واحدة لكل عنصر من الصفيف.
  • لا تحافظ الإشارة إلى ${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 .

7
Jonathan Leffler

الجملة الثانية من الفقرة الأولى تحت Parameter Expansion in 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
kojiro

قضية ذات صلة لم تتم تغطيتها أعلاه. اقتباس متغير متغير يبدو أنه يغير الأشياء لـ 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
3
nortally

حسنًا ، أعرف أن تغليف متغير يساعدك على التعامل مع شيء مثل:

${groups%example}

أو بناء جملة من هذا القبيل ، حيث تريد أن تفعل شيئا مع المتغير الخاص بك قبل إرجاع القيمة.

الآن ، إذا رأيت الكود ، فكل السحر موجود في الداخل

${groups[@]}

السحر موجود لأنه لا يمكنك الكتابة فقط: $groups[@]

أنت تضع المتغير الخاص بك داخل {} لأنك تريد استخدام أحرف خاصة [] و @. لا يمكنك تسمية المتغير أو الاتصال به فقط: @ أو something[] لأن هذه الأحرف محجوزة لعمليات وأسماء أخرى.

2
Sierisimo