อักขระขึ้นบรรทัดใหม่ที่หายไปจากการทดแทนคำสั่งของฉันอยู่ที่ไหน


15

รหัสต่อไปนี้อธิบายสถานการณ์ได้ดีที่สุด เหตุใดบรรทัดสุดท้ายจึงไม่แสดงเอาต์พุตอักขระขึ้นบรรทัดใหม่ เอาต์พุตของแต่ละบรรทัดจะแสดงในความคิดเห็น ฉันใช้GNU bash เวอร์ชัน 4.1.5

     echo -n $'a\nb\n'                  | xxd -p  # 610a620a  
           x=$'a\nb\n'   ; echo -n "$x" | xxd -p  # 610a620a
     echo -ne "a\nb\n"                  | xxd -p  # 610a620a
x="$(echo -ne "a\nb\n")" ; echo -n "$x" | xxd -p  # 610a62

4
วิธีแก้ปัญหาสำหรับเวลาที่หายากเมื่อต้องการ:tmp=$(somecommand; echo a); tmp=${tmp%a}
Gilles 'หยุดความชั่วร้าย'


1
@Gilles: เป็นตัวอย่างของคุณด้านบน: tmp=$(somecommand; echo a)... สิ่งนี้ได้ผลักดันจุดกลับบ้านอย่างแน่นอน ... จนกว่าฉันจะเห็นตัวอย่างแนวโน้มของฉันจะยังคงใช้echo -n a... แต่แน่นอน! ไม่จำเป็นต้องมี-nเพราะคำสั่งเปลี่ยนตัวจะลบขึ้นบรรทัดใหม่นำมาใช้ในกรณีใด ๆ ! ... ขอบคุณ ...
Peter.O

คำตอบ:


18

ฟังก์ชั่นการแทนที่คำสั่ง$()(และลูกพี่ลูกน้องของมัน backtick) โดยเฉพาะลบบรรทัดใหม่ต่อท้าย นี่เป็นพฤติกรรมที่บันทึกไว้และคุณควรระวังเมื่อใช้โครงสร้าง

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

$ echo -n "$(echo -n 'a\nb')"
a
b

$ !! | xxd -p
610a62

$ echo -n  $(echo -n 'a\nb')
a b

$ !! | xxd -p   
612062

ในตัวอย่างที่สองผลลัพธ์ไม่ได้ถูกยกมาและขึ้นบรรทัดใหม่ถูกตีความว่าเป็นการแบ่งคำทำให้ปรากฏในเอาต์พุตเป็นช่องว่าง!


1
ขอบคุณคาเลบ .. ฉันได้ตระหนักถึงคำแยกเปลี่ยนช่องว่างที่จะเป็นพื้นที่เดียวเมื่อunquoted ... นั่นคือเหตุผลที่ผมรู้สึกประหลาดใจมากที่สุดที่จะเห็นการขึ้นบรรทัดใหม่ต่อท้ายของฉันหายไปแม้ว่าฉันได้ยกมามัน ... ตอนนี้ฉันรู้ว่า เป็นเพราะพฤติกรรม 'ปกติ' ของการแทนที่คำสั่งวางบรรทัดใหม่ต่อท้าย ... โอ้ดี c'est la vie .. และขอบคุณสำหรับลิงค์
Peter.O

6

เมื่อใช้การทดแทนคำสั่งเชลล์จะประมวลผลคำสั่งในเชลล์ย่อยส่งคืน stdout ในกระบวนการนี้อักขระ IFS สูญเสียความสำคัญ (ถ้าไม่มีการอ้างอิง) เนื่องจากการยกย่องส่งกลับคำที่แยกแบบธรรมดาดังนั้นคำต่อท้ายจะถูกลบออก ตัวอย่างเช่น:

$ echo "$(echo -e '\n')" | wc
 1       0       1

$ echo -e '\n' | wc
2       0       2

และในทางปฏิบัติมากกว่าpwdจะทำงานได้แม้ว่าชื่อไดเรกทอรีของคุณจะมีบรรทัดใหม่ในระหว่างนั้น แต่$(pwd)จะไม่ทำงาน

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


1
ขอบคุณ Philomath ... ตามตัวอย่างคุณเป็นคนแรกที่ผิด syntactically ('wc' ไม่ยอมรับสตริงเป็น arg) .. สำหรับตัวอย่างของคุณที่จะเข้าใจคนแรกอาจจะเป็นecho "$(echo -e '\n')" | wcที่ผล1   0   1เมื่อเทียบกับ2   0   2
Peter.O

@ เฟร็ด: โอ๊ะโอพิมพ์ผิด
Philomath

1
"แปลงเป็นช่องว่าง" ไม่ใช่คำอธิบายที่เหมาะสมมีเพียงบางส่วนเท่านั้นที่เกี่ยวข้อง โดยเฉพาะอย่างยิ่งคุณสามารถลบที่ว่างออกจาก IFS และสิ่งเหล่านั้นจะไม่ทำหน้าที่เป็นตัวคั่น ยิ่งกว่านั้นตัวอย่างของคุณอยู่ในหมวดหมู่เคสพิเศษและมีความแตกต่างระหว่าง$(pwd)และ"$(pwd)"ให้ดูที่คำตอบของ Caleb
Stéphane Gimenez

ข้อเสนอแนะ PS..Your ของการแก้ปัญหาตามปกติคือสิ่งที่ฉันต้องการที่จะล้างคนนี้ขึ้นมา ..
Peter.O

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