แม้ว่า @cyrus นั้นถูกต้อง แต่ก็ไม่ได้ตอบคำถามทั้งหมดและไม่มีคำอธิบายว่าเกิดอะไรขึ้น
ดังนั้นให้เดินผ่านมัน
ขึ้นบรรทัดใหม่ในสตริง
ก่อนกำหนดลำดับไบต์ที่คาดไว้:
$ { echo a; echo b; } | xxd
0000000: 610a 620a a.b.
ตอนนี้ใช้การแทนที่คำสั่ง (ส่วน 3.5.4)เพื่อพยายามจับลำดับไบต์นี้:
$ x=$( echo a; echo b; )
จากนั้นทำเสียงสะท้อนอย่างง่ายเพื่อตรวจสอบลำดับของไบต์:
$ echo $x | xxd
0000000: 6120 620a a b.
ดังนั้นดูเหมือนว่าการขึ้นบรรทัดใหม่ครั้งแรกจะถูกแทนที่ด้วยช่องว่างและการขึ้นบรรทัดใหม่บรรทัดที่สองยังคงเหมือนเดิม แต่ทำไม
ให้ดูสิ่งที่เกิดขึ้นจริงที่นี่:
ก่อนอื่น bash จะทำการขยายพารามิเตอร์เชลล์ (ส่วน 3.5.3)
อักขระ '$' จะแนะนำการขยายพารามิเตอร์การทดแทนคำสั่งหรือการขยายเลขคณิต ชื่อพารามิเตอร์หรือสัญลักษณ์ที่จะขยายอาจอยู่ในวงเล็บปีกกาซึ่งเป็นทางเลือก แต่ทำหน้าที่ปกป้องตัวแปรที่จะขยายจากอักขระทันทีหลังจากนั้นซึ่งสามารถตีความได้ว่าเป็นส่วนหนึ่งของชื่อ
จากนั้น bash จะทำการแบ่งคำ (ส่วน 3.5.7)
เชลล์จะสแกนผลลัพธ์ของการขยายพารามิเตอร์การทดแทนคำสั่งและการขยายเลขคณิตที่ไม่ได้เกิดขึ้นภายในเครื่องหมายคำพูดคู่เพื่อแยกคำ
เชลล์ปฏิบัติต่ออักขระแต่ละตัวของ $ IFS เป็นตัวคั่นและแยกผลลัพธ์ของการขยายตัวอื่น ๆ ออกเป็นคำในอักขระเหล่านี้ หาก IFS ไม่ได้ตั้งค่าไว้หรือเป็นค่าที่แน่นอน ...
ถัดไป bash จะถือว่าเป็นคำสั่งแบบง่าย (ส่วน 3.2.1)
คำสั่งง่ายๆคือชนิดของคำสั่งที่พบบ่อยที่สุด มันเป็นเพียงลำดับของคำที่คั่นด้วยช่องว่างซึ่งถูกยกเลิกโดยหนึ่งในโอเปอเรเตอร์ควบคุมของเชลล์ (ดูคำจำกัดความ) คำแรกโดยทั่วไประบุคำสั่งที่จะดำเนินการกับคำที่เหลือเป็นอาร์กิวเมนต์ของคำสั่งนั้น
คำจำกัดความของช่องว่าง (ส่วนที่ 2 - คำจำกัดความ)
ช่องว่างหรืออักขระแท็บ
ในที่สุด bash จะเรียกใช้คำสั่งภายในecho (ส่วนที่ 4.2 - คำสั่ง Bash Builtin)
... ส่งออก args คั่นด้วยช่องว่างสิ้นสุดด้วยการขึ้นบรรทัดใหม่ ...
ดังนั้นเพื่อสรุปบรรทัดใหม่จะถูกลบออกโดย Word Splitting จากนั้น echo จะได้รับ 2 args, "a" และ "b" จากนั้นส่งออกแยกโดยช่องว่างและลงท้ายด้วย newline
ทำสิ่งที่ @cyrus แนะนำ (และปราบปรามการขึ้นบรรทัดใหม่จาก echo ด้วย -n) ผลลัพธ์จะดีกว่า:
$ echo -n "$x" | xxd
0000000: 610a 62 a.b
ขึ้นบรรทัดใหม่ที่ท้ายสตริง
มันยังไม่สมบูรณ์แบบแม้ว่าการขึ้นบรรทัดใหม่จะหายไป มองใกล้ที่การทดแทนคำสั่ง (ส่วน 3.5.4) :
Bash ทำการขยายตัวโดยการรันคำสั่งและแทนที่การทดแทนคำสั่งด้วยเอาต์พุตมาตรฐานของคำสั่งโดยลบบรรทัดใหม่ต่อท้าย
ตอนนี้มันชัดเจนว่าทำไมบรรทัดใหม่หายไปการทุบตีอาจถูกหลอกเพื่อรักษามัน หากต้องการทำสิ่งนี้ให้เพิ่มสตริงเพิ่มเติมที่ส่วนท้ายและลบออกเมื่อใช้ตัวแปร:
$ x=$( echo a; echo b; echo -n extra )
$ echo -n "${x%extra}" | xxd
0000000: 610a 620a a.b.
TL; DR
เพิ่มส่วนพิเศษที่ส่วนท้ายของเอาต์พุตและตัวแปรเครื่องหมายคำพูด:
$ cat /no/file/here 2>&1 | xxd
0000000: 6361 743a 202f 6e6f 2f66 696c 652f 6865 cat: /no/file/he
0000010: 7265 3a20 4e6f 2073 7563 6820 6669 6c65 re: No such file
0000020: 206f 7220 6469 7265 6374 6f72 790a or directory.
$ cat /no/file/here 2>&1 | cksum
3561909523 46
$
$ var=$( cat /no/file/here 2>&1; rc=${?}; echo extra; exit ${rc})
$ echo $?
1
$
$ echo -n "${var%extra}" | xxd
0000000: 6361 743a 202f 6e6f 2f66 696c 652f 6865 cat: /no/file/he
0000010: 7265 3a20 4e6f 2073 7563 6820 6669 6c65 re: No such file
0000020: 206f 7220 6469 7265 6374 6f72 790a or directory.
$ echo -n "${var%extra}" | cksum
3561909523 46