ความแตกต่างระหว่าง $ {} และ $ () ในเชลล์สคริปต์


26
$ echo $(date)
Thu Jul 2 16:33:11 SGT 2015
$ echo ${date}

$ name=foo
$ echo $(name)
ksh: name:  not found

$ echo ${name}
foo

ดูเหมือนว่า $ {variable} จะเหมือนกับ $ variable ในขณะที่ $ () คือการดำเนินการคำสั่ง เหตุใดจึงใช้ $ {} จากนั้น


คำตอบ:


36

$(command)คือ "การทดแทนคำสั่ง" ในขณะที่คุณดูเหมือนจะเข้าใจมันรันcommand, จับเอาท์พุทและแทรกเข้าไปในบรรทัดคำสั่งที่มี$(…); เช่น,

$ ls -ld $(date +%B).txt
-rwxr-xr-x  1 Noob Noob    867 Jul  2 11:09 July.txt

${parameter}คือ“ การทดแทนพารามิเตอร์” ข้อมูลจำนวนมากสามารถพบได้ในหน้า man ของ shell, bash (1) , ภายใต้หัวข้อ“ Parameter Expansion ”:

${parameter}
    ค่าของพารามิเตอร์ถูกแทนที่ วงเล็บปีกกาจำเป็นเมื่อพารามิเตอร์เป็นพารามิเตอร์ตำแหน่งที่มีมากกว่าหนึ่งหลักหรือเมื่อพารามิเตอร์ตามด้วยอักขระที่ไม่ควรตีความว่าเป็นส่วนหนึ่งของชื่อ

สำหรับพารามิเตอร์ตำแหน่งดู“ พารามิเตอร์ตำแหน่ง ” ด้านล่าง ในการใช้งานทั่วไปดังที่แสดงในคำตอบอื่น ๆ parameterเป็นชื่อตัวแปร ${…}รูปแบบตามที่ระบุไว้ในตอนท้ายของย่อหน้าข้างต้นจะช่วยให้คุณได้รับค่าของตัวแปร (เช่น) และปฏิบัติตามได้ทันทีด้วยตัวอักษรตัวเลขหรือขีด:$variable_name

$ animal = cat
$ echo $ สัตว์
                                # ไม่มีตัวแปรเช่น "สัตว์"
$ echo $ {animal} s
แมว
$ echo $ animal_food
                                # ไม่มีตัวแปรเช่น "animal_food"
$ echo $ {animal} _food
อาหารแมว

คุณสามารถทำสิ่งนี้ด้วยเครื่องหมายคำพูด:

$ echo "$ animal" s
แมว

หรือเป็นแบบฝึกหัดในตัวเลือกคุณสามารถใช้ตัวแปรตัวที่สอง:

$ plural = s
$ echo $ สัตว์ $ plural
แมว

แต่นี่เป็นเพียงขั้นตอนที่ 1 ย่อหน้าถัดไปในหน้า man นั้นน่าสนใจแม้ว่าจะเป็นความลับเล็กน้อย:

หากอักขระตัวแรกของพารามิเตอร์ เป็นเครื่องหมายอัศเจรีย์ ( !) จะมีการแนะนำระดับการเปลี่ยนทิศทางของตัวแปร Bash ใช้ค่าของตัวแปรที่เกิดขึ้นจากพารามิเตอร์ที่เหลือ เป็นชื่อของตัวแปร จากนั้นตัวแปรนี้จะถูกขยายและค่านั้นจะถูกใช้ในการทดแทนที่เหลือแทนที่จะเป็นค่าพารามิเตอร์เอง นี้เรียกว่าการขยายตัวทางอ้อม     (ข้อยกเว้น)    เครื่องหมายอัศเจรีย์ต้องทำตามวงเล็บปีกกาด้านซ้ายในทันทีเพื่อแนะนำการอ้อม

ฉันไม่แน่ใจว่าฉันจะทำให้ชัดเจนขึ้นได้อย่างไรยกเว้นด้วยตัวอย่าง:

$ animal = cat
$ echo $ สัตว์
แมว
$ cat = tabby
$ echo $ cat
แมวลาย
$ echo $ {! animal}
tabby                            # หาก $ animal คือ “ cat” ดังนั้น $ {! animal} คือ $ cat นั่นคือ “ tabby”

ลองเรียกขั้นตอนที่1½กัน มีสิ่งที่น่าสนใจมากมายที่คุณสามารถทำได้ในขั้นตอนที่ 2:

$ animal = cat
$ echo $ {# animal}
3                                # ความยาวสตริง
$ echo $ {สัตว์ / at / ow}
การแทนที่วัว                              #

คุณไม่สามารถทำสิ่งใด ๆ เหล่านี้หากไม่มีเครื่องหมายปีกกา{...}

พารามิเตอร์ตำแหน่ง

ลองพิจารณาตัวอย่างเทียมนี้:

$ cat myecho.sh
echo $ 1 $ 2 $ 3 $ 4 $ 5 $ 6 $ 7 $ 8 $ 9 $ 10 $ 11 $ 12 $ 13 $ 13 $ 14 $ 15
$ ./myecho.sh เฮ้ดอดเดิลแมวและซอตัวผู้วัวกระโดดข้ามดวงจันทร์
เฮ้ด็ดด็อลแมวและซอ, เฮ้เฮ้เฮ้เฮ้เฮ้เฮ้

เพราะเปลือกไม่เข้าใจ$10, $11ฯลฯ ให้การปฏิบัติราวกับว่ามันเป็น$10 ${1}0แต่มันก็เข้าใจ${10}, ${11}ฯลฯ , ตามที่กล่าวไว้ใน man page (“ พารามิเตอร์ตำแหน่งที่มีมากกว่าหนึ่งหลัก”)

แต่อย่าเขียนสคริปต์แบบนั้นจริงๆ มีวิธีที่ดีกว่าในการจัดการกับรายการอาร์กิวเมนต์ที่ยาวนาน

ดังกล่าวข้างต้น (พร้อมกับรูปแบบอื่น ๆ อีกมากมายของโครงสร้าง) จะกล่าวถึงที่มีความยาวมากขึ้นในหน้าคนของเปลือก, ทุบตี (1)${parameter…something_else}

หมายเหตุเกี่ยวกับคำคม

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

$ filename = "สถานรับเลี้ยงเด็ก rhyme.txt"
$ ls -ld $ {filename}
ls: ไม่สามารถเข้าถึงสถานรับเลี้ยงเด็ก: ไม่มีไฟล์หรือไดเรกทอรีดังกล่าว
ls: ไม่สามารถเข้าถึง rhyme.txt: ไม่มีไฟล์หรือไดเรกทอรีดังกล่าว
$ ls -ld "$ filename"
-rwxr-xr-x 1 Noob Noob 5309 ก.ค. 2 11:09 สถานรับเลี้ยงเด็ก rhyme.txt

ที่ยังใช้กับพารามิเตอร์ตำแหน่ง (เช่นอาร์กิวเมนต์บรรทัดคำสั่ง; เช่น"$1") และการทดแทนคำสั่ง:

$ ls -ld $ (วันที่ "+% B% Y"). txt
ls: ไม่สามารถเข้าถึงกรกฎาคม: ไม่มีไฟล์หรือไดเรกทอรีดังกล่าว
ls: ไม่สามารถเข้าถึง 2015.txt: ไม่มีไฟล์หรือไดเรกทอรีดังกล่าว
$ ls -ld "$ (วันที่" +% B% Y "). txt"
-rwxr-xr-x 1 Noob Noob 687 ก.ค. 2 11:09 กรกฎาคม 2558.txt

ดูทุบตีคำพูดที่ไม่ใช้ Escape บนแทนคำสั่ง สำหรับตำราสั้น ๆ เกี่ยวกับการมีปฏิสัมพันธ์ระหว่างคำพูดและ...$()


ขอบคุณสำหรับตัวอย่างที่ยอดเยี่ยม คุณช่วยอธิบายการใช้! ในตัวอย่างของคุณ ฉันไม่เข้าใจว่ามันทำงานอย่างไร
Noob

@Noob: !ตกลงฉันเนื้อหาเกี่ยวกับการใช้งานของ
G-Man

ขอบคุณ อย่างใด! สัตว์จริง ๆ แล้วหมายถึงแมวตัวแปรแทนแมวที่มีคุณค่า คุณสามารถบอกให้ฉันรู้ได้อย่างไรว่า / at กลายเป็น "c" ใน echo $ {สัตว์ / at / ow} คำอธิบายของคนที่ทุบตีนั้นเป็นอย่างไร ... ฉันไม่รู้ ยากที่จะเข้าใจ.
Noob

นอกจากนี้คุณสามารถอธิบายเพิ่มเติมวลีนี้ - "$ {พารามิเตอร์} ค่าของพารามิเตอร์ถูกแทนที่" แทนที่ด้วยอะไร ถ้าพารามิเตอร์เป็นผลไม้และค่าของมันคือ apple - fruit = apple ดังนั้น $ {fruit} - apple ถูกแทนที่ด้วย ?? ไม่ได้รับความหมายแทนนี่
Noob

@Nob:“ ${!animal}หมายถึงตัวแปร$catแทนค่า cat” จริง ๆ แล้วนั่นคือประเด็น “ / กลายเป็น“ c” ใน echo $ {สัตว์ / at / ow} อย่างไร” Huh? / at ไม่กลายเป็น "c"; “ cat” กลายเป็น“ cow” เมื่อ“ at” ถูกแทนที่ด้วย“ ow”
G-Man

7

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

    $ string=foo
    $ echo ${string}bar
      foobar
    $ echo $stringbar

    $ 

ดังนั้นวงเล็บปีกกาจึงเป็นวิธีการแทนตัวแปรเพื่อให้ได้ชื่อของตัวแปรใหม่


4

ฉันมักจะเห็นมันเป็นปกติในสตริง สิ่งนี้จะไม่ทำงาน:

var="a"
echo "$varRAW_STRING"

แต่จะ:

var="a"
echo "${var}RAW_STRING"

อย่างที่คุณพูดถูกต้อง$()ใช้เพื่อรันคำสั่ง:

dir_contents=$(ls)

คุณสามารถใช้ backticks ได้ด้วย แต่ฉันพบว่ามีประโยชน์$()หลายอย่างมากกว่า สำหรับสิ่งหนึ่ง backticks ไม่สามารถซ้อนกันได้ (ง่าย)

date_directory=`ls `date '+%Y-%m-%d'`` # Makes no sense
date_directory=$(ls $(date '+%Y-%m-%d')) # Much better

ที่จริงแล้วแบ็คทิกเกอร์สามารถซ้อนกันได้: date_directory=`ls \`date '+%Y-%m-%d'\``. แต่มันก็น่าเกลียดมาก $(…)มีความชัดเจนและใช้งานง่ายกว่ามาก
G-Man

ตกลงยุติธรรมพอ เป็นไปได้ทางเทคนิค แต่ฉันไม่สามารถจินตนาการได้ว่าทุกคนที่เคยทำเพียงเพราะการอ่าน
bytesized

1
ดี`…`เป็น(เท่านั้น) ไวยากรณ์สำหรับแทนคำสั่งสำหรับปีก่อนที่จะถูกคิดค้นเพื่อให้คุณไม่จำเป็นที่จะคิดอะไร - คนไม่ได้ $(…)
G-Man

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