Sự khác biệt giữa 'ls' và 'echo $ (ls)'


27

Hãy xem xét hai mẫu vỏ

$ ls
myDoc.html
SomeDirectory
someDoc.txt

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

Cái đầu tiên thực thi ls, theo tôi hiểu, sẽ nối các nội dung của thư mục làm việc hiện tại vào stdouttệp (đó là những gì thiết bị đầu cuối hiển thị). Điều này có đúng không?

Thứ hai lấy giá trị của lslệnh (có nghĩa là nội dung của thư mục làm việc hiện tại) và in nó vào stdouttệp. Điều này có đúng không?

Tại sao hai lệnh cho đầu ra khác nhau?


1
bởi vì bạn đang làm tiếng vang Đầu ra của ls là đầu vào cho lệnh echo.
ankidaemon

Đây là hành vi "mong đợi", mà ai đó sẽ giải thích ngay sau đây. Tuy nhiên, bạn có chắc chắn rằng lschính nó không cung cấp cho bạn nhiều mặt hàng trên mỗi dòng không?
roaima

@roaima cột ls là phần còn lại của "tính năng" bsd. phiên bản unix in một mục trên mỗi dòng
Steve Cox

@SteveCox Tôi chưa sử dụng UNIX đúng từ đầu SVR4 nên bộ nhớ của tôi hơi mơ hồ. Cảm ơn bạn đã nhắc nhở.
roaima

Tại sao không echo *?
8 tháng 8

Câu trả lời:


70

Khi bạn chạy lệnh này:

ls

thiết bị đầu cuối hiển thị đầu ra của ls.

Khi bạn chạy lệnh này:

echo $(ls)

Shell nắm bắt đầu ra $(ls)và thực hiện phân tách từ trên nó. Với mặc định IFS, điều này có nghĩa là tất cả các chuỗi khoảng trắng, bao gồm các ký tự dòng mới, được thay thế bằng một khoảng trống. Đó là lý do tại sao đầu ra echo $(ls)xuất hiện trên một dòng.

Để thảo luận nâng cao về phân tách từ, hãy xem Câu hỏi thường gặp của Greg .

Ngăn chặn chia tách từ

Shell không thực hiện phân tách từ trên chuỗi trong dấu ngoặc kép. Do đó, bạn có thể ngăn chặn việc tách từ và giữ lại đầu ra đa dòng bằng:

echo "$(ls)"

ls và sản lượng đa dòng

Bạn có thể nhận thấy rằng lsđôi khi in nhiều hơn một tệp trên mỗi dòng:

$ ls
file1  file2  file3  file4  file5  file6

Đây là mặc định khi đầu ra của lsđi đến một thiết bị đầu cuối. Khi đầu ra không trực tiếp đến thiết bị đầu cuối, lsthay đổi mặc định của nó thành một tệp trên mỗi dòng:

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

Hành vi này được ghi lại trong man ls .

Một sự tinh tế khác: thay thế lệnh và các dòng mới

$(...)sự thay thế lệnh và shell sẽ loại bỏ các ký tự dòng mới khỏi đầu ra của sự thay thế lệnh . Điều này thường không đáng chú ý vì theo mặc định, echothêm một dòng mới vào cuối đầu ra của nó. Vì vậy, nếu bạn mất một dòng mới từ cuối $(...)và bạn có được một dòng từ echođó, không có thay đổi. Tuy nhiên, nếu đầu ra của lệnh của bạn kết thúc bằng 2 hoặc nhiều ký tự dòng mới trong khi chỉ echothêm lại một ký tự , thì đầu ra của bạn sẽ thiếu một hoặc nhiều dòng mới. Ví dụ, chúng ta có thể sử dụng printfđể tạo các ký tự dòng mới. Lưu ý rằng cả hai lệnh sau, mặc dù có số dòng mới khác nhau, tạo ra cùng một đầu ra của một dòng trống:

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

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

$ 

Hành vi này được ghi lại trongman bash .

Một điều ngạc nhiên khác: mở rộng tên đường dẫn, hai lần

Hãy tạo ba tệp:

$ touch 'file?' file1 file2

Quan sát sự khác biệt giữa ls file?echo $(ls file?):

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

Trong trường hợp echo $(ls file?), tệp toàn cầu file?được mở rộng hai lần , khiến tên tệp file1file2xuất hiện hai lần trong đầu ra. Điều này là do, như Jeffiekins chỉ ra, việc mở rộng tên đường dẫn được thực hiện trước tiên bởi trình bao trước khi lsđược chạy và sau đó lại thực hiện trướcecho được chạy.

Việc mở rộng tên đường dẫn thứ hai có thể bị chặn nếu chúng ta sử dụng dấu ngoặc kép:

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

4
Ngoài ra, bằng cách sử dụng, isattybạn có thể kiểm tra xem thiết bị xuất chuẩn có phải là tty hay không, ls sử dụng điều này để xác định có nên sử dụng màu hay không.
Janus Troelsen

1
Không phải các trích dẫn không khớp trong đoạn mã cuối cùng? Hoặc $( )thực sự cung cấp một rào cản cú pháp để bạn không cần phải nội suy?
con mèo

6
@cat $()cung cấp một rào cản cú pháp.
Random832

2
Một sự khác biệt quan trọng hơn: nếu bất kỳ tên tệp nào chứa ký tự đại diện, echolệnh sẽ mở rộng ký tự đại diện . Ví dụ, nếu lstrả về tập tin? file1 file2 , sau đó echo $(ls)sẽ trả lại file? file1 file2 file1 file2 .
Jeffiekins

1
@Jeffiekins echokhông mở rộng ký tự đại diện; Shell thực hiện trước khi chuyển phần mở rộng kết quả thành đối số echo.
chepner

0

ls nhận biết nếu nó đang gửi đầu ra đến một thiết bị đầu cuối hay không.

Thực hiện lstại một dấu nhắc lệnh sẽ ghi vào giả của bạn. Chuyển hướng đầu ra của lsnhìn chung sẽ không đi đến giả, và lssẽ định dạng đầu ra khác nhau.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.