ความแตกต่างระหว่างสามคำสั่งเหล่านี้คืออะไร?
echo `date`
echo "`date`"
echo '`date`'
ฉันสับสนในความแตกต่างจริง ๆ ฉันคิดว่าเมื่อ 'อยู่ใกล้ก็หมายความว่ามันเป็นสตริงดังนั้นเสียงก้องจะออกสตริงdate
แทนที่จะแสดงวันที่?
ความแตกต่างระหว่างสามคำสั่งเหล่านี้คืออะไร?
echo `date`
echo "`date`"
echo '`date`'
ฉันสับสนในความแตกต่างจริง ๆ ฉันคิดว่าเมื่อ 'อยู่ใกล้ก็หมายความว่ามันเป็นสตริงดังนั้นเสียงก้องจะออกสตริงdate
แทนที่จะแสดงวันที่?
คำตอบ:
`date 'จะขยายไปยังเอาต์พุตของdate
คำสั่ง อย่างไรก็ตามมันจะลบอักขระช่องว่างเพิ่มเติมในสถานที่ที่มีอักขระช่องว่างต่อเนื่องมากกว่าหนึ่งตัวในเอาต์พุต (นี่เป็นเพราะการทดแทนคำสั่งขึ้นอยู่กับการแยกคำและเนื่องจากวิธีการที่echo
คำสั่งจัดการหลายอาร์กิวเมนต์)
ใน"` date` "เครื่องหมายคำพูดคู่เป็นเครื่องหมายคำพูดที่อ่อนแอดังนั้นพวกเขาจะขยายตัวแปร (ลอง" $ PWD ") และดำเนินการทดแทนคำสั่ง ผลลัพธ์ของการขยายจะถูกส่งผ่านเป็นอาร์กิวเมนต์เดียวไปยังecho
คำสั่งโดยมีการเว้นวรรคติดต่อกัน: นั่นคือการแยกคำไม่ได้ดำเนินการ
ใน'`date' 'เครื่องหมายคำพูดเดี่ยวเป็นเครื่องหมายคำพูดที่แข็งแกร่งดังนั้นพวกเขาจะไม่อนุญาตให้มีการขยายตัวของตัวแปรหรือการทดแทนคำสั่งภายในพวกเขา
อ้างถึงลิงค์นี้สำหรับคำอธิบายเพิ่มเติม
แก้ไขจุดเป็นครั้งแรกที่ชี้ให้เห็นอย่างถูกต้องโดยไมเคิล Suelmann ในความคิดเห็นด้านล่าง
date
คำสั่งเปล่า
ทั้งสอง
echo `date`
และ
echo "`date`"
จะแสดงวันที่ เอาต์พุตจากหลังดูเหมือนว่าเอาต์พุตจากการรันdate
ด้วยตัวเอง
แม้ว่าจะมีความแตกต่าง: "
ราคาที่อยู่ในเครื่องหมายคำพูด"
จะถูกส่งไปecho
เป็นอาร์กิวเมนต์เดียว เครื่องหมายคำพูดห่อหุ้มเอาต์พุตของคำสั่งทั้งหมดเป็นหนึ่งอาร์กิวเมนต์ เนื่องจากecho
เพียงพิมพ์คำสั่งตามลำดับโดยมีช่องว่างระหว่างนั้นโดยทั่วไปจะมีลักษณะเหมือนกัน
นี่คือตัวอย่างของความแตกต่างที่ลึกซึ้ง:
echo `date`
ผลิต:
Fri Nov 1 01:48:45 EST 2013
แต่:
echo "`date`"
ผลิต:
Fri Nov 1 01:48:49 EST 2013
โปรดทราบว่าช่องว่างสองช่องหลังจากนั้นNov
ถูกลดขนาดให้เหลือหนึ่งช่องว่างโดยไม่มีเครื่องหมายคำพูด นี่เป็นเพราะเชลล์แยกวิเคราะห์แต่ละองค์ประกอบที่คั่นด้วยช่องว่างและส่งผลลัพธ์ไปยัง echo เป็น 6 อาร์กิวเมนต์ เมื่อคุณพูดมันสะท้อนได้รับการโต้แย้งเดียวและคำพูดรักษาพื้นที่
สิ่งนี้มีความสำคัญมากกว่าในคำสั่งอื่นที่ไม่ใช่เสียงสะท้อน ตัวอย่างเช่นลองจินตนาการถึงคำสั่งfoo
ที่ต้องการอาร์กิวเมนต์สองตัวคือวันที่และที่อยู่อีเมล
สิ่งนี้จะทำงานในสถานการณ์นั้น:
foo "`date`" joeuser@example.com
แต่สิ่งนี้จะทำให้สคริปต์สับสนโดยการส่งอาร์กิวเมนต์ 7 ข้อ:
foo `date` joeuser@example.com
ใน POSIX shells `date`
เป็นรูปแบบการทดแทนคำสั่งโบราณ $(date)
ไวยากรณ์ที่ทันสมัย
ในทั้งสองกรณีพวกเขาขยายไปยังเอาต์พุตของdate
ด้วยอักขระขึ้นบรรทัดใหม่ที่ต่อท้ายที่ถูกปล้น (โดยที่เอาต์พุตนั้นไม่มีอักขระ NUL)
อย่างไรก็ตามเมื่อไม่อยู่ในเครื่องหมายคำพูดคู่และในบริบทรายการ (ตัวอย่างเช่นในการขัดแย้งกับคำสั่งง่าย ๆ เช่นecho
ในกรณีของคุณ) การขยายตัวนั้นขึ้นอยู่กับ:
แยก Word ที่นั่นคือ"การส่งออกของdate
ด้วยตัวอักษรขึ้นบรรทัดใหม่ปล้น"คือแยกตามมูลค่าปัจจุบันของ$IFS
ตัวแปร (โดยเริ่มต้นที่มีพื้นที่แท็บและการขึ้นบรรทัดใหม่ (และ NUL ด้วยzsh
)) เป็นหลายคำ
ตัวอย่างเช่นถ้าdate
เอาท์พุทFri 1 Nov 14:11:15 GMT 2013\n
(เหมือนมันมักจะอยู่ในสถานที่ภาษาอังกฤษและในแผ่นดินใหญ่เขตเวลาอังกฤษ) และ$IFS
ในปัจจุบันมี:
ที่จะแยกออกเป็น 3 คำ : Fri 1 Nov 14
, และ11
15 GMT 2013
zsh
) นั่นคือแต่ละคำที่เกิดจากการแยกข้างต้นมองหาอักขระตัวแทน ( *
, ?
, [...]
แม้ว่าเปลือกหอยบางคนมีมากขึ้น) และขยายไปยังรายการของชื่อไฟล์ที่ตรงกับรูปแบบเหล่านั้น ตัวอย่างเช่นหากการส่งออกของdate
เป็น?%? 33 */*/* UVC 3432
(เหมือนมันมักจะอยู่ในสถานที่ศุกร์และ UVC เขต) และ$IFS
เป็นค่าเริ่มต้น) แล้วที่จะขยายไปยังทุกที่ไม่ใช่ที่ซ่อนอยู่ 3 ชื่อไฟล์ตัวอักษรในไดเรกทอรีปัจจุบันที่มีตัวอักษรกลาง%
, 33
, ทุกไฟล์ที่ไม่ใช่ที่ซ่อนอยู่ในทุกไดเรกทอรีย่อยที่ไม่ใช่ที่ซ่อนอยู่ของทุกไดเรกทอรีย่อยที่ไม่ใช่ซ่อนของไดเรกทอรีปัจจุบันและUVC
3432
นั่นคือเหตุผล:
$IFS
เป็นอักขระที่คุณต้องการแยกset +f
เพื่อปิดการใช้งานอัญประกาศเดียวอ้างทุกอย่างดังนั้นทำให้อักขระ backtick ถูกถ่ายอย่างแท้จริง
ตัวอย่าง (การใช้-x
ทำให้ง่ายต่อการดูว่าเกิดอะไรขึ้น):
$ bash --norc -x
bash-4.2$ IFS=:
+ IFS=:
bash-4.2$ echo `date`
++ date
+ echo 'Fri 1 Nov 14' 42 '33 GMT 2013'
Fri 1 Nov 14 42 33 GMT 2013
bash-4.2$ echo "`date`"
++ date
+ echo 'Fri 1 Nov 14:42:41 GMT 2013'
Fri 1 Nov 14:42:41 GMT 2013
bash-4.2$ cd /lib/modules
+ cd /lib/modules
bash-4.2$ export TZ=UVC LC_ALL=vs_VS
+ export TZ=UVC LC_ALL=vs_VS
+ TZ=UVC
+ LC_ALL=vs_VS
bash-4.2$ unset -v IFS # get the default behaviour
+ unset -v IFS
bash-4.2$ echo `date`
++ date
+ echo '?%?' 33 3.10-2-amd64/build/arch 3.10-2-amd64/build/include 3.10-2-amd64/build/Makefile 3.10-2-amd64/build/Module.symvers 3.10-2-amd64/build/scripts 3.10-2-amd64/kernel/arch 3.10-2-amd64/kernel/crypto 3.10-2-amd64/kernel/drivers 3.10-2-amd64/kernel/fs 3.10-2-amd64/kernel/lib 3.10-2-amd64/kernel/mm 3.10-2-amd64/kernel/net 3.10-2-amd64/kernel/sound 3.10-2-amd64/source/arch 3.10-2-amd64/source/include 3.10-2-amd64/source/Makefile 3.10-2-amd64/source/scripts 3.10-2-amd64/updates/dkms 3.10-3-amd64/build/arch 3.10-3-amd64/build/include 3.10-3-amd64/build/Makefile 3.10-3-amd64/build/Module.symvers 3.10-3-amd64/build/scripts 3.10-3-amd64/kernel/arch 3.10-3-amd64/kernel/crypto 3.10-3-amd64/kernel/drivers 3.10-3-amd64/kernel/fs 3.10-3-amd64/kernel/lib 3.10-3-amd64/kernel/mm 3.10-3-amd64/kernel/net 3.10-3-amd64/kernel/sound 3.10-3-amd64/source/arch 3.10-3-amd64/source/include 3.10-3-amd64/source/Makefile 3.10-3-amd64/source/scripts 3.10-3-amd64/updates/dkms UVC 3432
?%? 33 3.10-2-amd64/build/arch 3.10-2-amd64/build/include 3.10-2-amd64/build/Makefile 3.10-2-amd64/build/Module.symvers 3.10-2-amd64/build/scripts 3.10-2-amd64/kernel/arch 3.10-2-amd64/kernel/crypto 3.10-2-amd64/kernel/drivers 3.10-2-amd64/kernel/fs 3.10-2-amd64/kernel/lib 3.10-2-amd64/kernel/mm 3.10-2-amd64/kernel/net 3.10-2-amd64/kernel/sound 3.10-2-amd64/source/arch 3.10-2-amd64/source/include 3.10-2-amd64/source/Makefile 3.10-2-amd64/source/scripts 3.10-2-amd64/updates/dkms 3.10-3-amd64/build/arch 3.10-3-amd64/build/include 3.10-3-amd64/build/Makefile 3.10-3-amd64/build/Module.symvers 3.10-3-amd64/build/scripts 3.10-3-amd64/kernel/arch 3.10-3-amd64/kernel/crypto 3.10-3-amd64/kernel/drivers 3.10-3-amd64/kernel/fs 3.10-3-amd64/kernel/lib 3.10-3-amd64/kernel/mm 3.10-3-amd64/kernel/net 3.10-3-amd64/kernel/sound 3.10-3-amd64/source/arch 3.10-3-amd64/source/include 3.10-3-amd64/source/Makefile 3.10-3-amd64/source/scripts 3.10-3-amd64/updates/dkms UVC 3432
bash-4.2$ echo "`date`"
++ date
+ echo '?%? 33 */*/* UVC 3432'
?%? 33 */*/* UVC 3432
หากเอาต์พุตมีอักขระ NUL พฤติกรรมจะแตกต่างกันไปจากเชลล์หนึ่งไปยังเชลล์: บางส่วนถูกเอาออกบางส่วนจะตัดทอนผลลัพธ์ที่อักขระ NUL ตัวแรกzsh
รักษาไว้ แต่โปรดทราบว่าคำสั่งภายนอกไม่สามารถรับอาร์กิวเมนต์ที่มี NUL ได้
ด้วย `date 'คุณจะได้รับผลลัพธ์ของการแยกวันที่เป็นหลายคำเนื่องจากการแยกคำเสร็จหลังจากการแทนที่คำสั่ง
ด้วย "` date` "คุณจะได้ผลลัพธ์จาก date เป็นหนึ่งคำ / พารามิเตอร์เนื่องจากมีการทดแทนคำสั่งระหว่างเครื่องหมายคำพูดคู่ แต่เอาต์พุตไม่ได้ถูกวิเคราะห์คำเพิ่มเติม เช่นเดียวกับการขยายตัวแปรเช่น "$ i" ในตัวอย่างด้านล่าง
ด้วย '' date '' คุณจะได้รับ `date 'ตามตัวอักษรเนื่องจากไม่มีการทดแทนคำสั่งระหว่างอัญประกาศเดี่ยว
บางทีความแตกต่างของทั้ง 3 แบบนี้จะปรากฏให้เห็นได้มากกว่านี้:
> for i in `date`; do echo "$i"; done
Fr
1.
Nov
12:25:30
CET
2013
> for i in "`date`"; do echo "$i"; done
Fr 1. Nov 12:25:38 CET 2013
> for i in '`date`'; do echo "$i"; done
`date`