$ VAR เทียบกับ $ {VAR} และเพื่อพูดหรือไม่พูด


139

ฉันเขียนได้

VAR=$VAR1
VAR=${VAR1}
VAR="$VAR1"
VAR="${VAR1}"

ผลลัพธ์ที่ได้สำหรับฉันทุกคนดูเหมือนจะเหมือนกัน ทำไมฉันต้องเขียนอย่างใดอย่างหนึ่ง? เหล่านี้ไม่ใช่พกพา / POSIX?

คำตอบ:


99

VAR=$VAR1VAR=${VAR1}เป็นรุ่นที่เรียบง่ายของ มีสิ่งที่สองที่สามารถทำได้ที่สิ่งแรกไม่สามารถทำได้เช่นอ้างอิงดัชนีอาร์เรย์ (ไม่ใช่แบบพกพา) หรือลบสตริงย่อย (POSIX-portable) ดูส่วนเพิ่มเติมเกี่ยวกับตัวแปรของ Bash Guide สำหรับผู้เริ่มต้นและการขยายพารามิเตอร์ในข้อมูลจำเพาะ POSIX

การใช้เครื่องหมายคำพูดรอบ ๆ ตัวแปรเหมือนในrm -- "$VAR1"หรือrm -- "${VAR}"เป็นความคิดที่ดี สิ่งนี้ทำให้เนื้อหาของตัวแปรเป็นหน่วยอะตอมมิก หากค่าตัวแปรมีช่องว่าง (เช่นตัวอักษรใน$IFSตัวแปรพิเศษช่องว่างโดยค่าเริ่มต้น) หรือตัวอักษรกลมและคุณไม่พูดมันแล้วแต่ละคำจะถูกพิจารณาสำหรับการสร้างชื่อไฟล์ (กลม) ซึ่งการขยายตัวทำให้ขัดแย้งกับสิ่งที่คุณ กำลังทำ

$ find .
.
./*r*
./-rf
./another
./filename
./spaced filename
./another spaced filename
./another spaced filename/x
$ var='spaced filename'
# usually, 'spaced filename' would come from the output of some command and you weren't expecting it
$ rm $var
rm: cannot remove 'spaced': No such file or directory
# oops! I just ran 'rm spaced filename'
$ var='*r*'
$ rm $var
# expands to: 'rm' '-rf' '*r*' 'another spaced filename'

$ find .
.
./another
./spaced filename
./another spaced filename
$ var='another spaced filename'
$ rm -- "$var"
$ find .
.
./another
./spaced filename

เกี่ยวกับการพกพา: ตามPOSIX.1-2008 ส่วน 2.6.2การจัดฟันแบบหยิกเป็นทางเลือก


@shawn อัปเดตคำถามของฉันเพราะฉันยังสงสัยเกี่ยวกับการพกพา
xenoterracide

@shawn: ฉันสงสัยว่าตัวอย่างของคุณถูกต้อง คุณมีตัวอย่างที่แท้จริงของเชลล์ที่var1=$varการขยายให้ข้อผิดพลาดหรือไม่?
alex

@alex: ขอบคุณ ฉันคิดว่าฉันได้ทดสอบมันในบรรทัดคำสั่ง แต่ฉันทำผิด ฉันเปลี่ยนตัวอย่าง
Shawn J. Goff

ด้วยตัวอย่างที่อัปเดตคุณควรจำไว้ว่าโดยทั่วไปคุณควรต้องการเวอร์ชันที่ยกมาเนื่องจากตัวอย่างนั้นเป็นตัวพิมพ์มุม
alex

9
@Shawn: ไม่จำเป็นต้องใส่เครื่องหมายคำพูดในการมอบหมาย พวกเขามีความจำเป็นในการใช้งานอื่น ๆ export VAR=$VAR1ส่วนใหญ่รวมทั้ง สำหรับวงเล็บปีกกามันเป็นทางเลือก (ตรวจสอบย่อหน้าที่สี่ของส่วนที่คุณอ้างถึงนี่เป็นกรณีในเชลล์ POSIX และ POSIX ทั้งหมด)
Gilles

60

${VAR}และ$VARเทียบเท่า สำหรับการขยายตัวแปรธรรมดาเหตุผลเดียวที่จะใช้${VAR}คือเมื่อการแยกวิเคราะห์จะจับอักขระจำนวนมากเกินไปในชื่อตัวแปรดังเช่นใน${VAR1}_$VAR2(ซึ่งไม่มีเครื่องหมายวงเล็บปีกกาจะเทียบเท่า${VAR1_}$VAR2) ขยายประดับส่วนใหญ่ ( ${VAR:=default}, ${VAR#prefix}... ) ต้องจัดฟัน

ในการกำหนดตัวแปร, การแยกฟิลด์ (เช่นการแยกที่ช่องว่างในค่า) และการขยายชื่อพา ธ (เช่น globbing) ถูกปิดดังนั้นจึงVAR=$VAR1เทียบเท่ากับVAR="$VAR1"ใน POSIX เชลล์ทั้งหมดและใน POSIX SH ทั้งหมดที่ฉันเคยได้ยิน . (การอ้างอิง POSIX: คำสั่งง่าย ๆ ) ด้วยเหตุผลเดียวกันที่VAR=*เชื่อถือได้กำหนดVARที่จะสายอักขระตัวอักษร*; แน่นอนVAR=a bชุดVARไปaตั้งแต่bเป็นคำที่แยกจากกันในสถานที่แรก โดยทั่วไปการพูดอัญประกาศคู่ไม่จำเป็นโดยที่ไวยากรณ์เชลล์คาดว่าจะมีคำเดียวตัวอย่างเช่นในcase … in (แต่ไม่ใช่ในรูปแบบ) แต่ถึงแม้คุณจะต้องระวัง: เช่น POSIX ระบุว่าเป้าหมายการเปลี่ยนเส้นทาง ( >$filename) ไม่จำเป็นต้องมีการอ้างถึงในสคริปต์ แต่เชลล์บางตัวที่รวมถึง bash นั้นจำเป็นต้องมีเครื่องหมายคำพูดคู่แม้ในสคริปต์ ดูเมื่อมีความจำเป็นต้องใช้การอ้างอิงสองครั้ง? เพื่อการวิเคราะห์ที่ละเอียดยิ่งขึ้น

คุณจำเป็นต้องใส่เครื่องหมายอัญประกาศคู่ในกรณีอื่น ๆ โดยเฉพาะในexport VAR="${VAR1}"(ซึ่งสามารถเขียนได้เท่ากันexport "VAR=${VAR1}") ในเชลล์จำนวนมาก (POSIX เปิดเคสนี้ไว้) ความคล้ายคลึงกันของคดีนี้ที่มีการมอบหมายอย่างง่ายและลักษณะกระจัดกระจายของรายการคดีที่คุณไม่ต้องการอัญประกาศเป็นเหตุผลที่ฉันแนะนำให้ใช้เครื่องหมายคำพูดคู่เว้นแต่ว่าคุณต้องการแยกและกลม


2
ตามกฎทั่วไปฉันมักจะพูดถึงการขยายตัวของตัวแปรเสมอแม้ว่าฉันจะรู้ว่าค่าจะไม่มีIFSตัวละครใด ๆเพราะฉันอยากอยู่ในนิสัย ข้อยกเว้นเดียวคือฉันไม่ได้อ้างอิงค่าเมื่อทำการกำหนดตัวแปร (ยกเว้นกรณีจำเป็นเช่นเมื่อค่ามีช่องว่าง) FOO=$(BAR=$(BAZ=blah; printf %s "${BAZ}"); printf %s "${BAR}")นี้จะทำให้การแก้ไขไวยากรณ์ไฮไลท์มีประโยชน์มากขึ้นเมื่อมีการแทนคำสั่งเช่น แทนที่จะทำทุกอย่างให้เป็นสี "สตริง" ฉันได้รับการเน้นไวยากรณ์ของรหัสที่ซ้อนกัน นี่คือเหตุผลที่ฉันหลีกเลี่ยง backticks
Richard Hansen

ในขณะที่>$fileตกลงในสคริปต์ POSIX จะไม่ทุบตีแม้ว่าจะไม่ใช่แบบโต้ตอบ (ยกเว้นว่ามีการบังคับใช้ POSIX $POSIXLY_CORRECTหรือ--posix... )
Stéphane Chazelas

ในขณะที่มันเป็นความจริงที่ไม่จำเป็นต้องใส่เครื่องหมายคำพูดในVAR=$VAR1บางครั้งฉันก็แปลกใจด้วยlocal VAR=$VAR1ซึ่งฉันจำได้ว่าทำงานแตกต่างกันในบางประเด็นอย่างน้อยเปลือกหอยบางอย่าง แต่ตู้เอทีเอ็มฉันไม่สามารถทำซ้ำความแตกต่าง
dubiousjim

ตกลงพบปัญหาที่ฉันจำได้ มันแสดงให้เห็นในหอยบางตัวเท่านั้น
dubiousjim

@dubiousjim local VAR=$VAR1เป็นเหมือนexport VAR=$VAR1มันขึ้นอยู่กับเปลือก
Gilles

8

คำอ้างอิง

พิจารณาว่าการใช้เครื่องหมายคำพูดคู่ใช้สำหรับการขยายตัวของตัวแปรและการใช้เครื่องหมายคำพูดเดี่ยวเพื่อการอ้างอิงที่แข็งแกร่งเช่นการขยายตัวของ Sans

การขยาย:

this='foo'
that='bar'
these="$this"
those='$that'

เอาท์พุท:

for item in "$this" "$that" "$these" "$those"; do echo "$item"; done
foo
bar
foo
$that

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

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

การแทน

ตอนนี้ให้พิจารณาด้วยว่าการสร้าง"${somevar}"จะใช้สำหรับการดำเนินการทดแทน กรณีการใช้งานหลายอย่างเช่นการเปลี่ยนและอาร์เรย์

เปลี่ยน (ลอก):

thisfile='foobar.txt.bak'
foo="${thisfile%.*}"   # removes shortest part of value in $thisfile matching after '%' from righthand side
bar="${thisfile%%.*}"  # removes longest matching

for item in "$foo" "$bar"; do echo "$item"; done
foobar.txt
foobar

เปลี่ยน (เปลี่ยน):

foobar='Simplest, least effective, least powerful'
# ${var/find/replace_with}
foo="${foobar/least/most}"   #single occurrence
bar="${foobar//least/most}"  #global occurrence (all)

for item in "$foobar" "$foo" "$bar"; do echo "$item"; done
Simplest, least effective, least powerful
Simplest, most effective, least powerful
Simplest, most effective, most powerful

อาร์เรย์:

mkdir temp
# create files foo.txt, bar.txt, foobar.txt in temp folder
touch temp/{foo,bar,foobar}.txt
# alpha is array of output from ls  
alpha=($(ls temp/*))

echo "$alpha"         #  temp/foo.txt
echo "${alpha}"       #  temp/foo.txt
echo "${alpha[@]}"    #  temp/bar.txt  temp/foobar.txt  temp/foo.txt
echo "${#alpha}"      #  12 # length of first element (implicit index [0])
echo "${#alpha[@]}"   #  3  # number of elements
echo "${alpha[1]}"    #  temp/foobar.txt # second element
echo "${#alpha[1])"   #  15 # length of second element

for item in "${alpha[@]}"; do echo "$item"; done
temp/bar.txt
temp/foobar.txt
temp/foo.txt

ทั้งหมดนี้เป็นเพียงการเกาพื้นผิวของโครงสร้างการ "${var}"ทดแทน การอ้างอิงที่ชัดเจนสำหรับการสคริปต์เชลล์ Bash คือการอ้างอิงออนไลน์แบบเรียลไทม์, TLDP โครงการเอกสารลินุกซ์https://www.tldp.org/LDP/abs/html/parameter-substitution.html


1
ข้อมูลมาก
orion elenzil

0
ls -la

lrwxrwxrwx.  1 root root      31 Nov 17 13:13 prodhostname
lrwxrwxrwx.  1 root root      33 Nov 17 13:13 testhostname
lrwxrwxrwx.  1 root root      32 Nov 17 13:13 justname

จบแล้ว:

env=$1
    if [ ! -f /dirname/${env}hostname ]

มูลค่าการกล่าวขวัญเป็นตัวอย่างที่ชัดเจนของการใช้ curlies

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