ความแตกต่างระหว่าง 'ls' และ 'echo $ (ls)'


27

พิจารณาตัวอย่างเปลือกสองอัน

$ ls
myDoc.html
SomeDirectory
someDoc.txt

และ

$ echo $(ls)
myDoc.html SomeDirectory someDoc.txt

ครั้งแรกที่ดำเนินการlsตามที่ฉันเข้าใจผนวกเนื้อหาของไดเรกทอรีการทำงานปัจจุบันไปยังstdoutไฟล์ (ซึ่งเป็นสิ่งที่ terminal แสดง) ถูกต้องหรือไม่

ที่สองใช้ค่าของlsคำสั่ง (ซึ่งหมายความว่าเป็นเนื้อหาของไดเรกทอรีการทำงานปัจจุบัน) และพิมพ์ไปยังstdoutไฟล์ ถูกต้องหรือไม่

ทำไมทั้งสองคำสั่งจึงให้เอาต์พุตที่ต่างกัน?


1
เพราะคุณกำลังทำเสียงสะท้อน เอาต์พุตของ ls เป็นอินพุตไปยังคำสั่ง echo
ankidaemon

นี่คือพฤติกรรม "คาดหวัง" ซึ่งบางคนจะอธิบายในไม่ช้า อย่างไรก็ตามคุณแน่ใจlsด้วยตัวเองว่าไม่ให้หลายรายการต่อบรรทัด
roaima

@roaima columnized ls เป็นส่วนที่เหลือของคุณสมบัติ "bsd" รุ่น unix พิมพ์หนึ่งรายการต่อบรรทัด
Steve Cox

@SteveCox ฉันไม่ได้ใช้ UNIX ที่เหมาะสมตั้งแต่ต้น SVR4 ดังนั้นหน่วยความจำของฉันจึงค่อนข้างมืด ขอบคุณสำหรับคำเตือน
roaima

ทำไมไม่echo *?
jdh8

คำตอบ:


70

เมื่อคุณเรียกใช้คำสั่งนี้:

ls

lsแสดงขั้วการส่งออกของ

เมื่อคุณเรียกใช้คำสั่งนี้:

echo $(ls)

เชลล์จับเอาท์พุทของ$(ls)และทำการแยกคำบนมัน ด้วยค่าเริ่มต้นIFSซึ่งหมายความว่าลำดับทั้งหมดของพื้นที่สีขาวรวมถึงอักขระขึ้นบรรทัดใหม่จะถูกแทนที่ด้วยช่องว่างเดียว นั่นคือสาเหตุที่ผลลัพธ์ของการecho $(ls)ปรากฏบนหนึ่งบรรทัด

สำหรับการสนทนาขั้นสูงของการแยกคำเห็นของเกร็กคำถามที่พบบ่อย

ระงับการแยกคำ

เชลล์ไม่ดำเนินการแยกคำในสตริงในเครื่องหมายคำพูด ดังนั้นคุณสามารถระงับการแยกคำและเก็บเอาท์พุทหลายบรรทัดด้วย:

echo "$(ls)"

ls และหลายเอาท์พุท

คุณอาจสังเกตเห็นว่าlsบางครั้งพิมพ์มากกว่าหนึ่งไฟล์ต่อบรรทัด:

$ ls
file1  file2  file3  file4  file5  file6

นี่เป็นค่าดีฟอลต์เมื่อเอาต์พุตของlsไปยังเทอร์มินัล เมื่อเอาต์พุตไม่ไปยังเทอร์มินัลโดยตรงให้lsเปลี่ยนค่าดีฟอลต์เป็นหนึ่งไฟล์ต่อบรรทัด:

$ echo "$(ls)"
file1
file2
file3
file4
file5
file6

man lsลักษณะการทำงานนี้จะถูกบันทึกไว้ใน

ความละเอียดอื่น: การทดแทนคำสั่งและการขึ้นบรรทัดใหม่

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

$ echo "$(printf "\n")"

$ echo "$(printf "\n\n\n\n\n")"

$ 

man bashลักษณะการทำงานนี้จะถูกบันทึกไว้ใน

แปลกใจอีกประการหนึ่ง: การขยายชื่อพา ธ สองครั้ง

ลองสร้างสามไฟล์:

$ touch 'file?' file1 file2

สังเกตความแตกต่างระหว่างls file?และecho $(ls file?):

$ ls file?
file?  file1  file2
$ echo $(ls file?)
file? file1 file2 file1 file2

ในกรณีของecho $(ls file?), glob ไฟล์file?จะขยายตัวเป็นครั้งที่สองทำให้ชื่อไฟล์file1และfile2ปรากฏให้เห็นเป็นครั้งที่สองในการส่งออก นี่เป็นเพราะตามที่ Jeffiekins ชี้ให้เห็นการขยายชื่อพา ธจะดำเนินการโดยเชลล์ก่อนที่จะlsถูกรันและจากนั้นอีกครั้งก่อนที่echoจะรัน

สามารถขยายชื่อพา ธที่สองได้หากเราใช้เครื่องหมายคำพูดคู่:

$ echo "$(ls file?)"
file?
file1
file2

4
นอกจากนี้การใช้isattyคุณสามารถตรวจสอบว่า stdout เป็น tty ได้หรือไม่ ls ใช้สิ่งนี้เพื่อพิจารณาว่าจะใช้สีหรือไม่
Janus Troelsen

1
คำพูดไม่ตรงกันในข้อมูลโค้ดสุดท้ายหรือไม่ หรือ$( )ให้กำแพงกั้นไวยากรณ์จริงดังนั้นคุณไม่จำเป็นต้องสอดแทรก?
แมว

6
@cat $()จัดเตรียมสิ่งกีดขวางทางไวยากรณ์
Random832

2
หนึ่งความแตกต่างที่สำคัญมากขึ้นถ้าชื่อไฟล์ใด ๆ ที่มีอักขระตัวแทนที่echoคำสั่งจะขยายตัวแทน ตัวอย่างเช่นถ้าlsส่งคืนไฟล์ file1 file2จากนั้นecho $(ls)จะส่งคืนไฟล์หรือไม่ file1 file2 file1 file2
Jeffiekins

1
@Jeffiekins echoไม่ขยายสัญลักษณ์แทน echoเปลือกไม่ก่อนที่จะผ่านการขยายตัวส่งผลให้เป็นข้อโต้แย้ง
chepner

0

ls ทราบหรือไม่ว่ากำลังส่งเอาต์พุตไปยังเทอร์มินัล

การดำเนินการlsที่พรอมต์คำสั่งจะเขียนถึง pseudo-tty ของคุณ การเปลี่ยนเส้นทางเอาต์พุตของlsโดยทั่วไปจะไม่เป็นการหลอกแบบและlsจะฟอร์แมตเอาต์พุตต่างกัน

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