เมื่อไหร่ที่เราต้องการเครื่องมือจัดฟันแบบโค้งรอบ ๆ ตัวแปรเชลล์


658

ในเชลล์สคริปต์เราจะใช้{}เมื่อขยายตัวแปร?

ตัวอย่างเช่นฉันได้เห็นสิ่งต่อไปนี้:

var=10        # Declare variable

echo "${var}" # One use of the variable
echo "$var"   # Another use of the variable

มีความแตกต่างอย่างมีนัยสำคัญหรือเป็นเพียงรูปแบบ? เป็นที่ต้องการมากกว่าหนึ่งอื่น ๆ ?

คำตอบ:


751

ในตัวอย่างนี้มันไม่สร้างความแตกต่าง อย่างไรก็ตาม{}in ${}จะมีประโยชน์หากคุณต้องการขยายตัวแปรfooในสตริง

"${foo}bar"

เนื่องจาก"$foobar"จะขยายตัวแปรที่ระบุโดยแทนfoobarแทน

การจัดฟันแบบขดก็ไม่จำเป็นเช่นกันเมื่อ:

  • การขยายองค์ประกอบอาร์เรย์เช่นเดียวกับใน ${array[42]}
  • ใช้การดำเนินการขยายพารามิเตอร์เช่นเดียวกับใน${filename%.*}(ลบส่วนขยาย)
  • การขยายพารามิเตอร์ตำแหน่งเกิน 9: "$8 $9 ${10} ${11}"

การทำเช่นนี้ทุกแทนเพียงในกรณีที่อาจเกิดขึ้นคลุมเครือสามารถได้รับการพิจารณาการเขียนโปรแกรมที่ดี นี่คือทั้งเพื่อความมั่นคงและเพื่อหลีกเลี่ยงความประหลาดใจเช่น$foo_$bar.jpgที่ไม่เห็นชัดเจนว่าขีดเส้นใต้กลายเป็นส่วนหนึ่งของชื่อตัวแปร


106
{}เป็นที่รู้จักกันการขยายตัวรั้ง ${}เรียกว่าการขยายตัวของตัวแปร พวกเขาทำสิ่งต่าง ๆ ฉันจะลงคะแนนให้คุณยกเว้นบิตที่ไม่มีส่วนขยาย
Spencer Rathbun

5
@NewUser " ดังนั้นนอกเหนือจากอาร์เรย์ก็ไม่จำเป็นจริงๆ " ไม่เช่นนั้นวงเล็บปีกกาจำเป็นสำหรับการขยายพารามิเตอร์ซึ่งเป็นโครงสร้างที่มีประโยชน์มากในการสร้างสคริปต์ ฉันได้เห็นสคริปต์ที่ไม่ดีและ awk จำนวนมากที่สามารถแทนที่ด้วยการขยายพารามิเตอร์เล็กน้อย
SiegeX

10
@caffinatedmonkey $()ถูกนำมาใช้ในการดำเนินการคำสั่งดังกล่าวmd5sum=$(md5sum foo.bin)จะจัดเก็บการส่งออกของmd5sum foo.binในตัวแปรตอนนี้สามารถเข้าถึงได้โดยใช้md5sum ${md5sum}นอกจากนี้ยังมี +1 และอื่น ๆ อีกมากมายในเจตนารมณ์ของ OP ที่กล่าวถึงว่าเป็นแนวปฏิบัติที่ดีที่จะต้องมีความชัดเจน!
L0j1k

11
@ L0j1k พูดของแน่ชัดฉันคิดว่ามันสำคัญที่จะต้องพูดถึงว่า$()รันคำสั่งของจากsubshell
Adrian Günter

2
@karatedog ${1:-20}เป็นรูปแบบของการขยายพารามิเตอร์ ที่นี่มันไม่ชัดเจนเพราะส่วนใหญ่จะใช้ตัวเลขและตัวดำเนินการทางคณิตศาสตร์ซึ่งหลอกให้เราคิดว่ามีส่วนเกี่ยวข้องทางคณิตศาสตร์ แต่จริงๆแล้วมันหมายถึงพารามิเตอร์ตำแหน่ง$1ซึ่งหากไม่ได้กำหนดไว้จะถูกแทนที่ด้วยค่าเริ่มต้น20(ไวยากรณ์คือ${variable:-default_value})
แอรอน

126

ตัวแปรที่มีการประกาศและได้รับมอบหมายโดยไม่ต้องและไม่มี$ {}คุณต้องใช้

var=10

มอบหมาย. เพื่อที่จะอ่านจากตัวแปร (ในคำอื่น ๆ 'ขยาย' ตัวแปร) $คุณต้องใช้

$var      # use the variable
${var}    # same as above
${var}bar # expand var, and append "bar" too
$varbar   # same as ${varbar}, i.e expand a variable called varbar, if it exists.

สิ่งนี้ทำให้ฉันสับสนในบางครั้ง - ในภาษาอื่นที่เราอ้างถึงตัวแปรในลักษณะเดียวกันโดยไม่คำนึงถึงว่าอยู่ทางซ้ายหรือขวาของการมอบหมาย แต่การเขียนสคริปต์เชลล์แตกต่างกัน$var=10อย่าทำในสิ่งที่คุณคิดว่าทำ!


34

คุณใช้{}สำหรับการจัดกลุ่ม เครื่องมือจัดฟันจะต้องทำการตรวจสอบองค์ประกอบของอาร์เรย์อีกครั้ง ตัวอย่าง:

dir=(*)           # store the contents of the directory into an array
echo "${dir[0]}"  # get the first entry.
echo "$dir[0]"    # incorrect

dir=(*)ฉันไม่เข้าใจบรรทัดแรก เท่าที่ฉันรู้dirคือคำสั่งที่สร้างขึ้นเพื่อแสดงรายการเนื้อหาไดเรกทอรี (เทียบเท่าls -C -b) คุณช่วยอธิบายได้มั้ย
Jarvis

1
ในการเขียนโปรแกรมเชลล์คำสั่งและอาร์กิวเมนต์ต้องแยกจากกันด้วยช่องว่าง ที่นี่คุณจะเห็นเครื่องหมายเท่ากับโดยไม่มีช่องว่างหมายถึงนี่เป็นการกำหนดตัวแปร dirคือชื่อของตัวแปรและวงเล็บถูกใช้เพื่อรวบรวมการขยายชื่อไฟล์*ลงในอาร์เรย์
เกล็นแจ็คแมน

27

คุณยังสามารถทำการปรับแต่งข้อความภายในวงเล็บปีกกา:

STRING="./folder/subfolder/file.txt"
echo ${STRING} ${STRING%/*/*}

ผลลัพธ์:

./folder/subfolder/file.txt ./folder

หรือ

STRING="This is a string"
echo ${STRING// /_}

ผลลัพธ์:

This_is_a_string

คุณถูกต้องใน "ตัวแปรปกติ" ไม่จำเป็นต้องใช้ ... แต่มันจะมีประโยชน์มากขึ้นสำหรับการดีบักและอ่านสคริปต์


11

จุดสิ้นสุดของชื่อตัวแปรมักจะมีความหมายโดยเว้นวรรคหรือขึ้นบรรทัดใหม่ แต่ถ้าเราไม่ต้องการช่องว่างหรือบรรทัดใหม่หลังจากพิมพ์ค่าตัวแปร? วงเล็บปีกกาบอกตัวแปลเชลล์โดยที่จุดสิ้นสุดของชื่อตัวแปรคือ

ตัวอย่างคลาสสิก 1) - ตัวแปรเชลล์ที่ไม่มีช่องว่างต่อท้าย

TIME=10

# WRONG: no such variable called 'TIMEsecs'
echo "Time taken = $TIMEsecs"

# What we want is $TIME followed by "secs" with no whitespace between the two.
echo "Time taken = ${TIME}secs"

ตัวอย่าง 2) classpath Java ที่มีไหรุ่น

# WRONG - no such variable LATESTVERSION_src
CLASSPATH=hibernate-$LATESTVERSION_src.zip:hibernate_$LATEST_VERSION.jar

# RIGHT
CLASSPATH=hibernate-${LATESTVERSION}_src.zip:hibernate_$LATEST_VERSION.jar

(คำตอบของ Fred ระบุไว้อย่างนี้แล้ว แต่ตัวอย่างของเขานั้นค่อนข้างนามธรรมเกินไป)


5

การจัดฟันแบบลอนนั้นจำเป็นสำหรับการเข้าถึงองค์ประกอบของอาเรย์และทำการขยายส่วนเสริม

เป็นการดีที่จะไม่ระมัดระวังและใช้{}สำหรับการขยายตัวแปรเชลล์แม้ว่าจะไม่มีขอบเขตสำหรับความคลุมเครือก็ตาม

ตัวอย่างเช่น:

dir=log
prog=foo
path=/var/${dir}/${prog}      # excessive use of {}, not needed since / can't be a part of a shell variable name
logfile=${path}/${prog}.log   # same as above, . can't be a part of a shell variable name
path_copy=${path}             # {} is totally unnecessary
archive=${logfile}_arch       # {} is needed since _ can be a part of shell variable name

ดังนั้นจะเป็นการดีกว่าถ้าเขียนสามบรรทัดดังนี้:

path=/var/$dir/$prog
logfile=$path/$prog.log
path_copy=$path

ซึ่งแน่นอนอ่านได้มากขึ้น

เนื่องจากชื่อตัวแปรไม่สามารถเริ่มต้นด้วยหลักเชลล์จึงไม่จำเป็นต้องใช้{}ตัวแปรที่มีหมายเลข (เช่น$1และ$2อื่น ๆ ) เว้นแต่ว่าจะมีการขยายตัวตามด้วยตัวเลข มันบอบบางเกินไปและทำให้ใช้งานอย่างชัดเจน{}ในบริบทดังกล่าว:

set app      # set $1 to app
fruit=$1le   # sets fruit to apple, but confusing
fruit=${1}le # sets fruit to apple, makes the intention clear

ดู:


1
It's good to be not over-cautious: ฉันสงสัยว่าคนส่วนใหญ่คิดอย่างไร ใช้วงเล็บปีกกาตลอดเวลาเพื่อให้คุณไม่ลืมพวกเขาเมื่อพวกเขาต้องการหรือใช้พวกเขาเมื่อจำเป็นเท่านั้นเพื่อปรับปรุงการอ่าน
Roger Dahl

1
ฉันคิดว่ามันเป็นการขาดความตระหนักที่นำไปสู่โปรแกรมเมอร์ที่ใช้ curlies แม้ว่าพวกเขาจะไม่ต้องการ ความเขลานี้คล้ายกับข้อผิดพลาดทั่วไปอื่น ๆ ที่ไม่ได้ใช้เครื่องหมายคำพูดคู่เพื่อป้องกันการแยกคำหรือการแยกคำโดยไม่ตั้งใจ ความจริงก็คือโปรแกรมเมอร์ไม่จริงจังกับการเขียนสคริปต์เชลล์มากเท่ากับภาษาสคริปต์อื่น ๆ เช่น Python และ Ruby
codeforester

1
จริงที่ โกรธสัตว์เลี้ยงของฉันคือทุกคนที่ดูเหมือนว่าจะคิดว่าตัวแปรทั้งหมดควรจะเป็นตัวพิมพ์ใหญ่ทั้งหมดในสคริปต์เปลือก :)
โรเจอร์ดาห์

2

การปฏิบัติตามคำแนะนำของ SierraX และ Peter เกี่ยวกับการจัดการข้อความวงเล็บปีกกา{}จะถูกใช้เพื่อส่งผ่านตัวแปรไปยังคำสั่งตัวอย่างเช่น

สมมติว่าคุณมีไฟล์sposi.txtที่บรรจุบรรทัดแรกของนวนิยายอิตาเลียนที่มีชื่อเสียง:

> sposi="somewhere/myfolder/sposi.txt"
> cat $sposi

ouput: quel ramo del lago di como che volge a mezzogiorno

ตอนนี้สร้างตัวแปรสองตัว:

# Search the 2nd word found in the file that "sposi" variable points to
> word=$(cat $sposi | cut -d " " -f 2)

# This variable will replace the word
> new_word="filone"

ตอนนี้แทนที่เนื้อหาตัวแปรwordด้วยหนึ่งในnew_wordภายในไฟล์ sposi.txt

> sed -i "s/${word}/${new_word}/g" $sposi
> cat $sposi

ouput: quel filone del lago di como che volge a mezzogiorno

คำว่า "ramo" ถูกแทนที่แล้ว


1
ใช้งานได้ดีเช่นกันโดยไม่ต้องใช้เครื่องหมายปีกกาแบบโค้งรอบตัวแปร
Armali

คุณอาจต้องการแก้ไขweel-known novelบิต โหวตขึ้นอย่างไรก็ตาม
gsl
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.