TL; DR
แต่เพียงผู้เดียวstuff
จะส่วนใหญ่อาจจะทำงานให้คุณ
คำตอบแบบเต็ม
เกิดอะไรขึ้น
เมื่อคุณวิ่งfoo $(stuff)
นี่คือสิ่งที่เกิดขึ้น:
stuff
วิ่ง;
- เอาท์พุท (stdout) แทนการถูกพิมพ์แทนที่
$(stuff)
ในการภาวนาของfoo
;
- จากนั้น
foo
รันอาร์กิวเมนต์บรรทัดคำสั่งจะขึ้นอยู่กับสิ่งที่stuff
ส่งคืน
นี้$(…)
กลไกที่เรียกว่า "คำสั่งเปลี่ยนตัว" ในกรณีของคุณคำสั่งหลักคือecho
โดยทั่วไปจะพิมพ์อาร์กิวเมนต์บรรทัดคำสั่งไปที่ stdout ดังนั้นสิ่งที่stuff
พยายามที่จะพิมพ์ไป stdout ถูกจับส่งผ่านไปecho
และพิมพ์ที่ stdout echo
โดย
หากคุณต้องการส่งออกของstuff
ที่จะพิมพ์ที่ stdout เพียงแค่เรียกใช้ stuff
แต่เพียงผู้เดียว
`…`
ไวยากรณ์ให้บริการวัตถุประสงค์เช่นเดียวกับ$(…)
(ภายใต้ชื่อเดียวกัน: "คำสั่งเปลี่ยนตัว") มีความแตกต่างไม่กี่แม้ว่าดังนั้นคุณจะไม่สามารถสุ่มสี่สุ่มห้าแลกเปลี่ยนพวกเขา ดูคำถามที่พบบ่อยนี้และคำถามนี้
ฉันควรหลีกเลี่ยงecho $(stuff)
ไม่ว่าจะเกิดอะไรขึ้น?
มีเหตุผลที่คุณอาจต้องการใช้echo $(stuff)
ถ้าคุณรู้ว่าคุณกำลังทำอะไรอยู่ ด้วยเหตุผลเดียวกันกับที่คุณควรหลีกเลี่ยงecho $(stuff)
หากคุณไม่รู้ว่าคุณกำลังทำอะไรอยู่
ประเด็นคือstuff
และecho $(stuff)
ไม่เทียบเท่า หมายถึงการเรียกผู้ประกอบการหลัง glob แยก + ในการส่งออกของมีค่าเริ่มต้นของstuff
$IFS
การอ้างถึงการทดแทนคำสั่งสองครั้งจะป้องกันสิ่งนี้ การอ้างคำสั่งเดียวเป็นการแทนคำสั่งทำให้ไม่เป็นการทดแทนคำสั่งอีกต่อไป
เมื่อต้องการสังเกตสิ่งนี้เมื่อถึงการแยกให้รันคำสั่งเหล่านี้:
echo "a b"
echo $(echo "a b")
echo "$(echo "a b")" # the shell is smart enough to identify the inner and outer quotes
echo '$(echo "a b")'
และสำหรับการโค้ง:
echo "/*"
echo $(echo "/*")
echo "$(echo "/*")" # the shell is smart enough to identify the inner and outer quotes
echo '$(echo "/*")'
ที่คุณสามารถดูecho "$(stuff)"
เทียบเท่า (-ish *) stuff
เพื่อ คุณสามารถใช้มันได้ แต่สิ่งที่ทำให้เกิดความซับซ้อนในลักษณะนี้คืออะไร?
ในทางกลับกันถ้าคุณต้องการให้เอาท์พุทของstuff
การแบ่ง + กลมแล้วคุณอาจพบว่าecho $(stuff)
มีประโยชน์ มันจะต้องมีการตัดสินใจอย่างมีสติแม้ว่า
มีคำสั่งที่สร้างผลลัพธ์ที่ควรได้รับการประเมิน (ซึ่งรวมถึงการแยก, การแยกและอื่น ๆ ) และดำเนินการโดยเชลล์ดังนั้นจึงeval "$(stuff)"
เป็นไปได้ (ดูคำตอบนี้ ) ฉันไม่เคยเห็นคำสั่งที่จำเป็นต้องใช้เอาต์พุตเพื่อรับการแบ่ง + การวนซ้ำเพิ่มเติมก่อนที่จะพิมพ์ จงใจใช้echo $(stuff)
ดูเหมือนว่าผิดปกติมาก
เกี่ยวกับvar=$(stuff); echo "$var"
อะไร
จุดดี. ตัวอย่างนี้:
var=$(stuff)
echo "$var"
ควรจะเทียบเท่ากับecho "$(stuff)"
เทียบเท่า (-ish *) stuff
เพื่อ หากเป็นรหัสทั้งหมดให้เรียกใช้stuff
แทน
อย่างไรก็ตามหากคุณจำเป็นต้องใช้เอาต์พุตstuff
มากกว่าหนึ่งครั้งแล้ววิธีนี้
var=$(stuff)
foo "$var"
bar "$var"
มักจะดีกว่า
foo "$(stuff)"
bar "$(stuff)"
แม้ว่าfoo
จะเป็นecho
และคุณได้รับecho "$var"
ในรหัสของคุณมันอาจจะดีกว่าที่จะเก็บไว้ในลักษณะนี้ สิ่งที่ต้องพิจารณา:
- ด้วยการ
var=$(stuff)
stuff
วิ่งครั้งเดียว; แม้ว่าคำสั่งจะเร็ว แต่การหลีกเลี่ยงการคำนวณผลลัพธ์เดียวกันสองครั้งเป็นสิ่งที่ถูกต้อง หรืออาจstuff
มีเอฟเฟกต์อื่นนอกเหนือจากการเขียนไปยัง stdout (เช่นการสร้างไฟล์ชั่วคราวการเริ่มบริการการเริ่มต้นเครื่องเสมือนการแจ้งเตือนเซิร์ฟเวอร์ระยะไกล) ดังนั้นคุณไม่ต้องการเรียกใช้หลายครั้ง
- หาก
stuff
สร้างเวลาขึ้นหรือการส่งออกค่อนข้างสุ่มคุณอาจได้รับผลลัพธ์ที่สอดคล้องกันจากและfoo "$(stuff)"
bar "$(stuff)"
หลังจากvar=$(stuff)
ค่าของ$var
ได้รับการแก้ไขและคุณสามารถมั่นใจfoo "$var"
และbar "$var"
รับอาร์กิวเมนต์บรรทัดคำสั่งที่เหมือนกัน
ในบางกรณีแทนที่จะเป็นfoo "$var"
คุณอาจต้องการ (ต้องการ) เพื่อใช้foo $var
โดยเฉพาะถ้าstuff
สร้างอาร์กิวเมนต์หลายตัวสำหรับfoo
(ตัวแปรอาเรย์อาจจะดีกว่าถ้าเชลล์ของคุณสนับสนุน) รู้อีกครั้งว่าคุณกำลังทำอะไรอยู่ เมื่อมันมาถึงecho
ความแตกต่างระหว่างecho $var
และecho "$var"
เป็นเช่นเดียวกับระหว่างและecho $(stuff)
echo "$(stuff)"
* เทียบเท่า ( -ish )?
ผมบอกว่าecho "$(stuff)"
เทียบเท่า (-ish) stuff
เพื่อ มีปัญหาอย่างน้อยสองเรื่องที่ทำให้ไม่เท่าเทียมกัน:
$(stuff)
วิ่งstuff
ใน subshell จึงดีกว่าที่จะพูดecho "$(stuff)"
จะเทียบเท่า (-ish) (stuff)
เพื่อ คำสั่งที่ส่งผลกระทบต่อเชลล์ที่พวกเขาทำงานหากอยู่ในเชลล์ย่อยจะไม่ส่งผลกระทบต่อเชลล์หลัก
ในตัวอย่างนี้stuff
คือa=1; echo "$a"
:
a=0
echo "$(a=1; echo "$a")" # echo "$(stuff)"
echo "$a"
เปรียบเทียบกับ
a=0
a=1; echo "$a" # stuff
echo "$a"
และด้วย
a=0
(a=1; echo "$a") # (stuff)
echo "$a"
อีกตัวอย่างเริ่มต้นด้วยstuff
การเป็นcd /; pwd
:
cd /bin
echo "$(cd /; pwd)" # echo "$(stuff)"
pwd
และการทดสอบstuff
และ(stuff)
รุ่น
echo
ไม่ใช่เครื่องมือที่ดีในการแสดงข้อมูลที่ไม่สามารถควบคุมได้ นี้เราได้พูดคุยเกี่ยวกับควรจะได้รับecho "$var"
printf '%s\n' "$var"
แต่เนื่องจากคำถามที่กล่าวถึงecho
และเนื่องจากวิธีแก้ปัญหาที่น่าจะเป็นไปได้มากที่สุดecho
ในตอนแรกฉันจึงตัดสินใจไม่แนะนำprintf
จนถึงตอนนี้
stuff
หรือ(stuff)
จะ interleave เอาต์พุต stdout และ stderr ขณะที่echo $(stuff)
จะพิมพ์เอาต์พุต stderr ทั้งหมดstuff
(ซึ่งรันก่อน) และจากนั้นเอาต์พุต stdout จะถูกย่อยโดยecho
(ซึ่งจะทำงานครั้งสุดท้าย)
$(…)
ตัดการขึ้นบรรทัดใหม่ต่อท้ายจากนั้นecho
เพิ่มกลับ ดังนั้นจะช่วยให้การส่งออกที่แตกต่างกว่าecho "$(printf %s 'a')" | xxd
printf %s 'a' | xxd
บางคำสั่ง ( ls
ตัวอย่าง) ทำงานแตกต่างกันไปขึ้นอยู่กับว่าเอาต์พุตมาตรฐานเป็นคอนโซลหรือไม่ จึงls | cat
ไม่เหมือนกันls
ไม่ ในทำนองเดียวกันจะทำงานที่แตกต่างกว่าecho $(ls)
ls
ใส่ls
กันในกรณีทั่วไปถ้าคุณมีการบังคับพฤติกรรมอื่น ๆ แล้วstuff | cat
จะดีกว่าecho $(ls)
หรือecho "$(ls)"
เพราะมันไม่ได้เรียกทุกประเด็นอื่น ๆ ที่กล่าวถึงที่นี่
สถานะการออกที่แตกต่างกันอาจเป็นไปได้ (กล่าวถึงเพื่อความสมบูรณ์ของคำตอบ wiki นี้สำหรับรายละเอียดดูคำตอบอื่นที่สมควรได้รับเครดิต)