Tại sao một số chương trình nhất định (như readlink) lấy đầu vào từ một đường ống?


7

Tôi có một liên kết tượng trưng đến một tập lệnh trong $PATHtập tin mà tôi muốn chỉnh sửa. Tôi đã quên filepath, vì vậy tôi đã cố gắng làm như sau:

$ which my_script_link | readlink

Tôi dự kiến ​​sẽ xuất ra filepath, nhưng thay vào đó nó xuất ra

> readlink: missing operand
> Try 'readlink --help' for more information

Tôi đã thấy hành vi tương tự trước đây trong các tình huống khác (như cố gắng đưa danh sách các tệp vào vim để chỉnh sửa). Tôi biết có những cách giải quyết, giống như một mạng con readlink $(which my_script_link), nhưng tôi muốn hiểu tại sao đường ống không hoạt động theo cách tôi nghĩ nó nên trong tình huống này.

Cảm ơn!

Câu trả lời:


11

Nói một cách đơn giản, vì chương trình không được viết để làm như vậy. Các lập trình viên phải quyết định xem phần mềm của họ có thể đọc từ STDIN hay không nếu nó yêu cầu một tệp đầu vào. Trong trường hợp readlink, mantrang của nó nêu (nhấn mạnh của tôi):

readlink - in các liên kết tượng trưng được giải quyết hoặc tên tệp chính tắc

Liên kết đọc SYNOPSIS
[TÙY CHỌN] ... TẬP TIN ...

Theo nguyên tắc chung, các chương trình lấy đầu vào từ STDIN được thiết kế để phân tích cú pháp đầu vào đó bằng cách nào đó. Khi bạn chuyển dữ liệu tới readlinknó sẽ nhận được một luồng văn bản và không biết phải làm gì với nó vì nó chỉ xử lý các tệp chứ không phải nội dung của chúng. Điều này cũng đúng với các chương trình như lshoặc cdhoặc cpvv Chỉ những chương trình xử lý luồng văn bản / dữ liệu mới có thể chấp nhận đầu vào từ một đường ống.


7

Việc một chương trình lấy đầu vào từ stdinhoặc làm đối số dòng lệnh tùy thuộc vào người thiết kế. Cả hai phương pháp đều có giá trị của chúng. Tuy nhiên, đối với các chương trình hoạt động nghiêm ngặt trên các tệp, việc chuyển tên tệp dưới dạng đối số dòng lệnh sẽ thuận tiện hơn là thông qua stdin.

Lý do rõ ràng nhất là trường hợp phổ biến, đó là chỉ chạy một chương trình trên một tệp, dễ gõ hơn: readlink filethay vì echo file | readlink.

Một vấn đề tinh vi hơn là tính đúng đắn. Vấn đề là khi tên tệp được truyền qua stdin, chương trình cần có khả năng phân biệt tên tệp này với tên tệp khác. Thông thường, điều này được thực hiện bằng cách giả sử tên tệp được phân tách bằng khoảng trắng hoặc dòng mới, nhưng điều này không chính xác vì tên tệp có thể bao gồm khoảng trắng. Một cách tốt hơn là tách tên tệp bằng byte rỗng, nhưng có thể bất tiện khi tạo danh sách các tệp được phân tách bằng null.

Truyền tên tệp trên dòng lệnh sẽ tránh được vấn đề này vì shell xử lý tất cả các phân tích cú pháp và trích dẫn cho chương trình. Bạn có thể nhập touch $'foo\nbar'touchxem chính xác đây là một tên tệp có chứa một dòng mới, mà không phải xử lý bất kỳ phân tích cú pháp hoặc trích dẫn đặc biệt nào.

Tất cả những gì đang được nói, nếu bạn muốn truyền tệp stdincho một chương trình cụ thể, bạn có thể. Đây là những gì xargsdành cho. xargscho phép bạn có một chương trình chỉ chấp nhận các đối số trên dòng lệnh và thay vào đó làm cho nó đưa các đối số của nó đi qua stdin.

Nói cách khác, nó cho phép bạn làm điều này : which my_script | xargs readlink.

Nếu bạn muốn luôn làm readlinkviệc theo cách này, bạn có thể tạo một bí danh : alias readlink="xargs readlink". Điều đó sẽ cho phép bạn gõ which my_script | readlink, như bạn muốn ban đầu.


Cảm ơn vì điều đó. Tôi không bao giờ hiểu xargs(không bao giờ bận tâm với Google về nó hoặc đọc trang người đàn ông), nhưng điều đó có ý nghĩa. Lần duy nhất tôi từng sử dụng xargs là mở danh sách các tệp trong vim, a la find *.rb | xargs vim. Bây giờ tôi hiểu những gì đang làm và tại sao nó hoạt động khi find *.rb | vimkhông. Cảm ơn!
Nathan Wallace

4

Đối với các lệnh không nhận đối số qua STDIN, bạn có thể sử dụng mã pragma này thay thế:

$ readlink $(which my_script_link)

Thí dụ

$ ln -s /bin/ls ~/bin/somecmd

Bây giờ hãy kiểm tra xem nó có trên không $PATH.

$ which somecmd
~/bin/somecmd

Hoặc cách ưa thích, sử dụng typeinstad của which:

$ type somecmd
somecmd is /home/saml/bin/somecmd

Hoặc chỉ là giá trị:

$ type -P somecmd 
/home/saml/bin/somecmd

Bây giờ chúng tôi chạy readlink:

$ readlink $(type -P somecmd)
/bin/ls

Tôi đã nói nhiều như vậy trong câu hỏi. Tôi tò mò, mặc dù, tại sao sẽ typethích hơn which?
Nathan Wallace

1
@NathanWallace - Tôi đã xác nhận rằng đây là cách để làm điều đó. Cũng nhấn mạnh việc sử dụng loại mà trong câu trả lời của tôi, terdon đã trình bày lý do. Xem Câu hỏi thường gặp về U & L này: unix.stackexchange.com/questions/85249/NH
slm
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.