ทำไมการแทนที่คำสั่งของเชลล์จึงฮุบไลน์อักขระบรรทัดใหม่?


25

ตามตัวอย่างต่อไปนี้และในคำถามล่าสุดของฉัน ในทุบตีอักขระขึ้นบรรทัดใหม่หายไปไหน ฉันต้องการรู้ว่า "ทำไม" มันเกิดขึ้น

  x="$(echo -ne "a\nb\n")" ; echo -n "$x" | xxd -p 
# Output is: 610a62 
# The trailing newline from the 'echo' command
#   has been "deleted" by Command Substitution

ฉันคิดว่าจะต้องมีเหตุผลที่สำคัญมากสำหรับการกระทำของเชลล์นั่นคือการแทนที่คำสั่งเพื่อลบข้อมูลบางส่วนออกจากผลลัพธ์ของคำสั่งมันเป็นการแทนที่ ...
แต่ฉันไม่สามารถเอาหัวของฉันไปรอบ ๆ สิ่งนี้ได้ สิ่งที่ตรงกันข้ามที่ควรทำ .. คือ เพื่อส่งผ่านคำสั่งกลับไปยังกระบวนการสคริปต์ ... การถือตัวละครหนึ่งตัวกลับดูแปลกสำหรับฉัน แต่ฉันคิดว่ามันมีเหตุผลที่สมเหตุสมผลสำหรับมัน ... ฉันกระตือรือร้นที่จะค้นหาว่าเหตุผลนั้นคืออะไร .. .


คำตอบ:


22

เพราะตอนแรกเชลล์ไม่ได้ตั้งใจให้เป็นภาษาโปรแกรมเต็มรูปแบบ

การลบส่วนท้าย\nออกจากเอาต์พุตคำสั่งค่อนข้างยากทีเดียว อย่างไรก็ตามเพื่อจุดประสงค์ในการแสดงผลคำสั่งเกือบทั้งหมดจะจบการทำงานด้วย\nดังนั้น ... จะต้องมีวิธีที่ง่ายในการลบออกเมื่อคุณต้องการใช้ในคำสั่งอื่น การกำจัดแบบอัตโนมัติด้วยการ$()ก่อสร้างเป็นทางเลือกที่เหมาะสม

ดังนั้นคุณอาจยอมรับคำถามนี้เป็นคำตอบ:

คุณจะพบวิธีง่ายๆในการลบการติดตาม\nหากสิ่งนี้ไม่ได้ทำโดยอัตโนมัติในคำสั่งต่อไปนี้หรือไม่?

> echo The current date is "$(date)", have a good day!

โปรดทราบว่าจำเป็นต้องมีการอ้างเพื่อป้องกันการชนสองช่องว่างที่อาจปรากฏในวันที่จัดรูปแบบ


1
หากสิ่งที่คุณพูดนั้นเป็นความจริงฉันก็จะตกตะลึงอย่างแน่นอน หากมีการshoptเปลี่ยนพฤติกรรมเริ่มต้นของคุณที่อธิบายไว้แล้วฉันจะมีความสุขอีกครั้ง ... ฉันคิดว่ามันยากที่จะคิดว่า Bash / Linux / Unix จะสร้างความเสียหายให้กับฟังก์ชั่นที่สำคัญเช่น "จับ stdout" เพียงเพราะdateไม่มี a โดยมี / ไม่มีตัวเลือก '\ n' ... การแก้ไขที่เห็นได้ชัดคือ bash (หรือเชลล์อื่น ๆ ) มีตัวเลือกshoptนี้ ... คุณรู้จักหรือไม่? .. และคุณมีลิงค์อ้างอิงที่พูดถึง whys และที่ใดของการออกเอกสารนี้? ... และทำไมไม่มีdateตัวเลือก \ n .. มันควร!
Peter.O

2
การแทนที่คำสั่งปรากฏในรุ่นแรกของ Bourne shellใน "รุ่นที่ 7" ของ Unix ในปี 1979 มันเป็นการปฏิวัติครั้งใหญ่และฉันเดาว่า (ฉันไม่ได้เกิด) ว่าไม่มีใครสนใจเกี่ยวกับการตามรอย '\ n' หรือไม่ ใช่คุณสามารถอ่านmanpageในเวลาน้อยกว่า 10 นาทีและดูเหมือนว่าจะไม่มีอะไรเปลี่ยนแปลงเกี่ยวกับการทดแทนคำสั่งจนกว่าจะถึงตอนนั้น ยกเว้น$()ไวยากรณ์ทางเลือกที่ต้องการ
Stéphane Gimenez

ขอบคุณ .. ฉันคิดว่านี่อาจเป็นปัญหาแบบดั้งเดิมและแน่นอนว่ากำลังสร้างความจริงที่ว่าฉันควรเริ่มดูการแทนที่คำสั่งของฉันอย่างระมัดระวังมากขึ้น จนถึงวันนี้ฉันจะต้องรอดพ้นจากการอธิษฐานวิงวอน .. "เหตุการณ์ลึกลับ" บางอย่างที่ฉันได้พบกำลังเริ่มทำให้เข้าใจแล้วตอนนี้ :) ... ดังนั้นในกรณีตรงกันข้ามกับ "วันที่" ของคุณวางตัวถ้า ฉันต้องการเก็บท้ายของฉัน \ n ฉันต้องเขียนโค้ดบางอย่างเช่นนี้ :( { echo -n "The current date is "; var="$(date; echo -e x)"; var="${var%x}"; echo -n "$var"; echo ", have a good day!"; }.. ไม่ว่าจะเป็น :) :)
Peter.O

PS อีกครั้งความคิดเห็นข้างต้นของฉัน: แน่นอนเพียงแค่dateจะทำงาน แต่ฉันเพียงแค่ใช้dateเป็นตัวอย่างของใด ๆคำสั่ง ..
Peter.O

'คำถาม' ของคุณเกี่ยวกับวันที่คือ promt ที่แข็งแกร่งที่สุดต่อความเข้าใจของฉัน 'ทำไม' Substution Command ทำในสิ่งที่มันจึงได้คำตอบที่ 'ดีที่สุด' สำหรับคำถามของฉัน ... และฉันก็ต้องการคำตอบที่ดีอื่น ๆ เช่นกัน ..
Peter.O

19

มันเป็นส่วนหนึ่งของมาตรฐาน :

เชลล์จะขยายการทดแทนคำสั่งโดยการดำเนินการคำสั่งในสภาพแวดล้อม subshell (ดูที่Shell Execution Environment ) และแทนที่การทดแทนคำสั่ง (ข้อความของคำสั่งรวมถึงการปิดล้อม "$ ()" หรือ backquotes) ด้วยเอาต์พุตมาตรฐานของคำสั่ง ลำดับของ <newline> อย่างน้อยหนึ่งรายการในตอนท้ายของการเปลี่ยนตัว

แน่นอนว่ามาตรฐานอาจถูกเขียนด้วยวิธีนี้เพราะนั่นเป็นวิธีการkshหรือบางสิ่ง แต่เป็นมาตรฐานและเป็นพฤติกรรมที่บันทึกไว้ หากคุณไม่ชอบให้ใช้ Perl หรืออย่างอื่นที่จะให้คุณขึ้นบรรทัดใหม่ต่อท้าย


บทสรุปที่ดี .. ขอบคุณ .. และลิงก์ช่วยอย่างแน่นอน
Peter.O

4
มาตรฐานเป็นเช่นนั้นเพราะนั่นเป็นวิธีที่เชลล์ Bourne ทำมันและเชลล์อื่น ๆ ตั้งแต่นั้นมา
Gilles 'หยุดชั่วร้าย'

6

มันสมเหตุสมผลสำหรับฉัน ขึ้นบรรทัดใหม่จะมีเฉพาะในสถานที่แรกในการส่งออกคำสั่งปกติเพื่อให้พรอมต์ปรากฏขึ้นหลังจากที่คำสั่งเสร็จสมบูรณ์ในบรรทัดใหม่ ขึ้นบรรทัดใหม่ไม่ได้เป็นส่วนหนึ่งของเอาต์พุตต้นฉบับในกรณีส่วนใหญ่มันอยู่ที่นั่นเพื่อทำให้หน้าจอเป็นระเบียบเรียบร้อยขึ้น เมื่อคุณแยกวิเคราะห์คำสั่งออกบรรทัดใหม่ที่ส่วนท้ายมักเป็นปัญหา เหตุใดwcคำสั่งจึงส่งข้อความ 2 บรรทัด โอ้มันไม่ออกมันออกมาหนึ่งอันตามด้วยการขึ้นบรรทัดใหม่ เมื่อคุณแยกวิเคราะห์wcคุณไม่ต้องการที่จะกังวลเกี่ยวกับความจริงที่ว่ามี 2 บรรทัดของการส่งออก - ไม่จริงมีเพียงหนึ่งเดียว


@EightBitTony:คำถามของฉันไม่ได้เกี่ยวข้องกับการแสดงบางสิ่งบางอย่างในเครื่องเทอร์มินัล .. หากมีการขึ้นบรรทัดใหม่เพื่อแสดงมันก็จะแสดงขึ้นบรรทัดใหม่ .. จุดที่เป็นปัญหาในที่นี้คือว่า\nในการstdoutสตรีมดั้งเดิมจะถูกลบออกโดยการทดแทนคำสั่ง กล่าวคือ ข้อมูลดั้งเดิมของฉัน (ข้อมูลที่สำคัญมาก) มีการขึ้นบรรทัดใหม่ตามหลังแม้ว่าข้อมูลจะอยู่ใน "เครื่องหมายคำพูด" ฉันมีเหตุผลที่เข้าใจวิธีการและเหตุผลของ Word แยกทำงาน แต่ฉันมีความคิดอย่างไม่มีเหตุผลที่คำสั่งเปลี่ยนตัวแถบเพียงการขึ้นบรรทัดใหม่ และมีเพียงลากคน
Peter.O

1
ไม่มีอะไรที่คุณพูดจะเปลี่ยนมุมมองของฉัน บ่อยครั้งที่บรรทัดใหม่สุดท้ายในเอาต์พุตใด ๆ นั้นมีไว้เพื่อวัตถุประสงค์ในการแสดงผลเท่านั้นไม่ใช่ส่วนหนึ่งของข้อมูล
EightBitTony

ผมเห็นด้วยกับมุมมองของคุณและมีมาตลอด ... ใช่การขึ้นบรรทัดใหม่ในตอนท้ายของการส่งออกคือเพื่อความสะดวกสบาย ... แต่ปัญหาของฉันมีอะไรจะทำอย่างไรกับการที่ ... ฉันถาม: ทำไมไม่สั่งชดเชยกวาดต้อนเอามันออกไป ? ... นี่เป็นสองประเด็นที่แตกต่างกัน .. บางทีคำถามของฉันควรเป็น: มีวิธีแก้ปัญหาในตัวหรือไม่? . เพราะต้องเขียนโค้ดสำหรับปัญหานี้โดยการเพิ่มตัวจำลอง dummy char, sucks! ... ฉันจะทำถ้าฉันต้องทำ แต่นี่เป็นครั้งแรกที่ฉันถูกทุบตีด้วยการทุบตี (และฉันตกอยู่ในภาวะตกใจ :) .. มันทำได้ดีมาก ...
Peter.O

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

ขอบคุณ .. ตอนนี้ฉันมีความคิดที่ดี .. ดูเหมือนว่าจะเป็นกรณีของการแทนที่คำสั่งเพียงแค่เอนไปทางสายใหม่ที่ต่อท้ายแทนที่จะดูแลพวกเขา .. ต้องมีภูมิปัญญาในการย้ายที่ฉันไม่ได้ "รู้สึก "แต่ฉันเคยคิดว่าการใช้ตัวอักษรตัวพิมพ์ใหญ่ทั้งหมดในชื่อไฟล์นั้นค่อนข้างแปลก / ไม่จำเป็น แต่ฉันคิดว่าเมื่อวันก่อนมันเป็นระบบที่ค่อนข้างสะดวกสบาย ... ฉันอาจจะ ดู (อย่างลึกซึ้งยิ่งขึ้น) สัมผัสและเหตุผลของพฤติกรรมคำสั่งเปลี่ยนตัวของบางวัน ...
Peter.O

1

ฉันพบปัญหาเดียวกันและพบตัวอย่างด้านล่าง ดูเหมือนว่าการอ้างถึงช่วยสถานการณ์อย่างน้อยก็สำหรับฉัน:

http://tldp.org/LDP/abs/html/commandsub.html

dir_listing=`ls -l`
echo $dir_listing     # unquoted

# Expecting a nicely ordered directory listing.

# However, what you get is:
# total 3 -rw-rw-r-- 1 bozo bozo 30 May 13 17:15 1.txt -rw-rw-r-- 1 bozo
# bozo 51 May 15 20:57 t2.sh -rwxr-xr-x 1 bozo bozo 217 Mar 5 21:13 wi.sh

# The newlines disappeared.


echo "$dir_listing"   # quoted
# -rw-rw-r--    1 bozo       30 May 13 17:15 1.txt
# -rw-rw-r--    1 bozo       51 May 15 20:57 t2.sh
# -rwxr-xr-x    1 bozo      217 Mar  5 21:13 wi.sh

2
นี่คือสิ่งที่เกิดขึ้นที่นี่ สมมติว่าคุณมีตัวแปรที่มีสตริง$str "a b c"ถ้าคุณผ่าน$strไปยังechoไม่ทราบราคา ( echo $str) echoจะได้รับa, bและcสามข้อโต้แย้งที่แยกต่างหาก เปลือกของคุณไม่สนใจช่องว่างระหว่างข้อโต้แย้ง จากนั้นจะพิมพ์ข้อโต้แย้งที่มีพื้นที่แยกแต่ละคนเพื่อให้คุณได้รับecho "a b c"หากคุณส่งผ่านตัวแปรechoโดยมีเครื่องหมายคำพูดล้อมรอบมัน ( echo "$str") เชลล์จะเห็นว่ามันเป็นหนึ่งอาร์กิวเมนต์และechoรับมันดังนั้นช่องว่างจะไม่ยุบ เช่นเดียวกับการขึ้นบรรทัดใหม่

1
ปัญหาที่ผู้โพสต์ดั้งเดิมมีคือการขึ้นบรรทัดใหม่ (เพียงบรรทัดสุดท้าย) ของคำสั่งของเขาจะหายไป ยกเว้นว่าผ่าน-nแฟล็กechoเพิ่มการขึ้นบรรทัดใหม่ให้กับเอาต์พุตดังนั้น echos ทั้งสองในตัวอย่างของคุณมีการขึ้นบรรทัดใหม่ อย่างไรก็ตามหากคุณลองecho -n $dir_listingหรือecho -n "$dir_listing"คุณจะเห็นว่าไม่มีบรรทัดใหม่ต่อท้ายถูกพิมพ์โดยมีหรือไม่มีเครื่องหมายอัญประกาศล้อมรอบ$dir_listingซึ่งแนะนำว่าปัญหาอยู่ที่การทดแทนคำสั่ง

2
tldp เป็นสถานที่ที่ล้าสมัยอย่างยิ่งที่จะเรียนรู้เกี่ยวกับเชลล์สคริปต์ ลองใช้ Wooledge Bash Wiki แทน mywiki.wooledge.org/BashGuide
Wildcard

-1

ทำไมecho "hello "(โดยไม่ใส่เครื่องหมายอัญประกาศ) โกยพื้นที่? เชลล์จะเห็นค่าของอักขระ IFS เป็นตัวคั่นดังนั้นอักขระที่ต่อท้าย (ซึ่งไม่ได้คั่นสิ่งใด ๆ ) จะถูกลบออก ยกย่องการทดแทนเป็นเพียงการดำเนินการธรรมดา commends ใน sub-shell ดังนั้นจึงเป็นไปตามตรรกะเดียวกัน


@Philomath:ทั้งสองx="hello  "และx="hello     "จะสูญเสียช่องว่างต่อท้ายทั้งหมดของพวกเขาหากมีการแสดงผ่านecho $x... อย่างไรก็ตามเฉพาะการขึ้นบรรทัดใหม่ล่าสุดของคำสั่ง Substituton หายไป
Peter.O

แปลกฉันไม่เห็นความแตกต่างในทุบตีฉันใด ๆ (ตรวจสอบกับ xxd)
PHILOMATH

ฉันเพิ่งตรวจสอบเรื่องนี้ ... มันลบ newlines ต่อท้ายทั้งหมด ... ฉันกำลังทดลองกับ IFS เมื่อฉันได้รับความประทับใจนั้นดังนั้นให้ยกเลิกอันนั้น :) :) แต่คำถามยังคงอยู่ ... มันเป็นส่วนพื้นฐาน การแยกคำเพื่อย่อพื้นที่หลาย ๆ แห่งไปยังพื้นที่เดียวและเพื่อลบช่องว่างชั้นนำและต่อท้ายโดยสิ้นเชิงฉันสามารถเข้าใจได้ แต่ฉันไม่เข้าใจว่าทำไมด้วยการแทนที่คำสั่งมันจะตัดเฉพาะ \ n เท่านั้นและไม่ใช่ช่องว่างทั้งหมด (เช่น ด้วยการแยกคำ) และทำไมเพียงปลายท้าย? .. และเหนือสิ่งอื่น: "การแทนที่คำสั่ง" เป็นเพียงการทดแทนบางส่วน .. ทำไม?
Peter.O

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