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')" | xxdprintf %s 'a' | xxd
บางคำสั่ง ( lsตัวอย่าง) ทำงานแตกต่างกันไปขึ้นอยู่กับว่าเอาต์พุตมาตรฐานเป็นคอนโซลหรือไม่ จึงls | catไม่เหมือนกันlsไม่ ในทำนองเดียวกันจะทำงานที่แตกต่างกว่าecho $(ls)ls
ใส่lsกันในกรณีทั่วไปถ้าคุณมีการบังคับพฤติกรรมอื่น ๆ แล้วstuff | catจะดีกว่าecho $(ls)หรือecho "$(ls)"เพราะมันไม่ได้เรียกทุกประเด็นอื่น ๆ ที่กล่าวถึงที่นี่
สถานะการออกที่แตกต่างกันอาจเป็นไปได้ (กล่าวถึงเพื่อความสมบูรณ์ของคำตอบ wiki นี้สำหรับรายละเอียดดูคำตอบอื่นที่สมควรได้รับเครดิต)