ฉันจะทำให้ตัวดำเนินการ bash backtick เก็บบรรทัดใหม่ในเอาต์พุตได้อย่างไร


36

มีวิธีที่จะทำให้ทุบตีไม่กินบรรทัดใหม่ในการทดแทน backtick หรือไม่?

ตัวอย่างเช่น:

var=`echo line one && echo line two`
echo $var

line one line two

สิ่งที่ฉันต้องการคือ

var=`echo line one && echo line two` # plus some magic
echo $var

line one
line two

คำตอบ:


54

มันไม่ได้เป็นปัญหากับ backticks เปลี่ยนตัว แต่ด้วยecho; คุณต้องอ้างอิงตัวแปรเพื่อให้ตัวควบคุมทำงาน:

$ var=`echo line one && echo line two`
$ echo "$var"
line one
line two

ดังนั้นทุบตีลบบรรทัดใหม่ในขณะที่การขยายตัวแปรในคำสั่งภาระ? และพฤติกรรมนั้นสามารถปิดการใช้งานโดยการอ้างอิงตัวแปร?
slowpoison

7
bashดำเนินการพารามิเตอร์การขยายตัวก่อนที่จะดำเนินการคำสั่งอ้างเป็นสิ่งจำเป็นที่จะแจ้งให้ทราบbashว่าคุณต้องการพิมพ์สายเดียวและไม่ได้ 4 คำ ( line, one, lineและtwo) ลองดู: และecho a <many spaces> b echo "a <many spaces> b"ลองดูคำตอบนี้ด้วย
cYrus

5

แม้ว่า @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

0

ฉันจะใส่ความเห็นเล็ก ๆ ไว้ในคำตอบของ cYrus แต่ฉันยังไม่สามารถแสดงความคิดเห็นกับคำตอบที่ฉันคิด ดังนั้นฉันจะทำซ้ำส่วนหนึ่งของ cYrus คำตอบของเขาเพื่อความสมบูรณ์

สิ่งที่เกิดขึ้นในบรรทัดแรกของรหัสของคุณก็คือการทดแทน backticks จะกินบรรทัดใหม่ต่อท้ายของเอาต์พุตของคำสั่งทดแทนตามที่บันทึกไว้ แต่จะไม่กินบรรทัดใหม่ระหว่างสองบรรทัดของคุณ

สิ่งที่เกิดขึ้นในโค้ดบรรทัดที่สองคือ echo ถูกเรียกใช้โดยมีสองอาร์กิวเมนต์คือบรรทัดแรกและบรรทัดที่สอง การอ้างอิงตัวแปรที่ขยายจะแก้ไขได้ว่า: echo "$var"วิธีนี้จะรักษาบรรทัดใหม่ระหว่างบรรทัดที่หนึ่งและบรรทัดที่สอง และechoจะเพิ่มขึ้นบรรทัดใหม่ตามค่าเริ่มต้นเพื่อให้ทุกอย่างเรียบร้อย

ตอนนี้ถ้าคุณต้องการที่จะรักษาบรรทัดใหม่ต่อท้ายทั้งหมดในการส่งออกของแทนคำสั่งบางวิธีแก้ปัญหาคือการเพิ่มสายอื่นที่ส่งออกซึ่งคุณสามารถลบหลังจากแทนคำสั่งให้ดูที่นี่

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