Tại sao một số lệnh không được đọc từ đầu vào tiêu chuẩn của họ?


19

Tôi tự hỏi những gì khi chúng ta nên sử dụng đường ống và khi nào chúng ta không nên.

Ví dụ, để giết một số quy trình xử lý tệp pdf, các thao tác sau sẽ không hoạt động bằng cách sử dụng đường ống dẫn:

ps aux | grep pdf | awk '{print $2}'|kill

Thay vào đó, chúng tôi chỉ có thể làm điều đó với các cách sau:

kill $(ps aux| grep pdf| awk '{print $2}')

hoặc là

ps aux | grep pdf | awk '{print $2}'| xargs kill

Theo man bash(phiên bản 4.1.2):

The standard output of command is connected via a pipe to the standard input of command2.

Đối với kịch bản trên:

  • stdin của greplà stdout của ps. Điều đó làm việc.
  • stdin của awklà stdout của grep. Điều đó làm việc.
  • stdin của killlà stdout của awk. Điều đó không hiệu quả.

Stdin của lệnh sau luôn nhận đầu vào từ thiết bị xuất chuẩn của lệnh trước.

  • Tại sao nó không hoạt động với killhoặc rm?
  • Có gì khác nhau giữa kill, rmđầu vào với grep, awkđầu vào?
  • Có quy định nào không?

1
Đây không phải là một câu trả lời nhưng bạn có thể muốn có một cái nhìn tại pgrep, pkillkillalllệnh.
terdon

2
@terdon: Tôi chỉ sử dụng kịch bản trên để hiển thị vấn đề đường ống, tôi hiểu điều đó pgrepvà phần còn lại có thể đạt được điều này một cách hoàn hảo :)
sylye

Câu trả lời:


17

Có hai cách phổ biến để cung cấp đầu vào cho các chương trình:

  • cung cấp dữ liệu cho STDIN của các quy trình
  • chỉ định đối số dòng lệnh

killchỉ sử dụng đối số dòng lệnh. Nó không đọc từ STDIN. Các chương trình thích grepawkđọc từ STDIN (nếu không có tên tệp nào được đưa ra làm đối số dòng lệnh) và xử lý dữ liệu theo các đối số dòng lệnh của chúng (mẫu, câu lệnh, cờ, ...).

Bạn chỉ có thể chuyển sang STDIN của các quy trình khác, không thể ra lệnh đối số dòng.

Quy tắc chung là, các chương trình sử dụng STDIN để xử lý lượng dữ liệu tùy ý. Tất cả các tham số đầu vào bổ sung hoặc, nếu thường chỉ có một ít, được truyền bởi các đối số dòng lệnh. Nếu dòng lệnh có thể rất dài, ví dụ đối với awkcác văn bản chương trình dài , thường có khả năng đọc chúng từ các tệp chương trình bổ sung ( -ftùy chọn awk).

Để sử dụng STDOUT của các chương trình làm đối số dòng lệnh, sử dụng $(...)hoặc trong trường hợp có nhiều dữ liệu xargs. findcũng có thể điều này trực tiếp với -exec ... {} +.

Để hoàn thiện: Để viết các đối số dòng lệnh vào STDOUT, hãy sử dụng echo.


1
Làm thế nào để chúng ta biết một lệnh sẽ chỉ lấy đối số chứ không phải STDIN? Có một cách có hệ thống hoặc lập trình hơn là đoán hoặc đọc từ trang người đàn ông? Bằng cách chỉ đọc trang man, tôi không thể có bất kỳ manh mối cụ thể nào về việc liệu lệnh có thể hoặc không thể lấy STDIN hay không, vì STDIN cũng là một phần của các đối số từ cách trình bày của một trang nam. Chẳng hạn, gziptrong SYNOPSIS, người ta không nói rằng nó phải lấy một LỌC TÊN làm đầu vào. Tôi đang tìm kiếm có một cách hệ thống hơn để xác định điều đó.
sylye

Ngoài ra còn có đối số "-" có nghĩa là "stdin" (hoặc "stdout") cho một số lệnh.
Emmanuel

xargsChính xác là không cho phép bạn "dẫn đến các đối số dòng lệnh"?
T. Verron

@ T.Verron có, đây là nhiệm vụ của xargs. Nó gọi lệnh nếu cần thiết hơn một lần (kích thước dòng lệnh bị giới hạn) và có nhiều tùy chọn khác.
jofel

2
Văn bản của mô tả sẽ mô tả cách bạn có thể sử dụng chương trình. Chẳng hạn, gzip nói: "Chương trình gzip nén và giải nén các tệp bằng mã hóa Lempel-Ziv (LZ77). Nếu không có tệp nào được chỉ định, gzip sẽ nén từ đầu vào tiêu chuẩn hoặc giải nén thành đầu ra tiêu chuẩn." Nếu một trang người đàn ông không đề cập đến đầu vào tiêu chuẩn, nó sẽ không sử dụng nó.
Alan Shutko

16

Đây là một câu hỏi thú vị và nó liên quan đến một phần của triết lý Unix / Linux.

Vì vậy, sự khác biệt giữa các chương trình như là những gì grep, sed, sorttrên một mặt và kill, rm, lsmặt khác? Tôi thấy hai khía cạnh.

Các bộ lọc khía cạnh

  • Loại chương trình đầu tiên cũng được gọi là bộ lọc . Họ lấy một đầu vào, từ một tệp hoặc từ STDIN, sửa đổi nó và tạo ra một số đầu ra, chủ yếu là STDOUT. Chúng có nghĩa là được sử dụng trong một đường ống với các chương trình khác làm nguồn và đích.

  • Loại chương trình thứ hai hoạt động trên một đầu vào, nhưng đầu ra mà chúng đưa ra thường không liên quan đến đầu vào. killkhông có đầu ra khi nó hoạt động thường xuyên, cũng không ls. Chỉ cần có một giá trị trở lại để hiển thị thành công. Họ thường không nhận đầu vào từ STDIN, nhưng chủ yếu cung cấp đầu ra cho STDOUT.

Đối với các chương trình như ls, khía cạnh bộ lọc không hoạt động tốt. Nó chắc chắn có thể có một đầu vào (nhưng không cần một đầu vào) và đầu ra có liên quan chặt chẽ với đầu vào đó, nhưng nó không hoạt động như một bộ lọc. Tuy nhiên, đối với loại chương trình đó, khía cạnh khác vẫn hoạt động:

Các ngữ nghĩa khía cạnh

  • Đối với các bộ lọc, đầu vào của chúng không có ý nghĩa ngữ nghĩa . Họ chỉ đọc dữ liệu, sửa đổi dữ liệu, dữ liệu đầu ra. Không quan trọng đây là danh sách các giá trị số, một số tên tệp hoặc mã nguồn HTML. Ý nghĩa của dữ liệu này chỉ được cung cấp bởi mã bạn cung cấp cho bộ lọc: regex for grep, quy tắc cho awkhoặc chương trình Perl.

  • Đối với các chương trình khác, thích killhoặc ls, đầu vào của họ có một ý nghĩa , một ngoại diên . killmong đợi số quá trình, lsmong đợi tập tin hoặc tên đường dẫn. Họ không thể xử lý dữ liệu tùy ý và chúng không có nghĩa. Nhiều người trong số họ thậm chí không cần bất kỳ đầu vào hoặc tham số, như ps. Họ thường không đọc từ STDIN.

Một người có thể có thể kết hợp hai khía cạnh này: Bộ lọc là một chương trình có đầu vào không có ý nghĩa ngữ nghĩa cho chương trình.

Tôi chắc chắn tôi đã đọc về triết lý này ở đâu đó, nhưng tôi không nhớ bất kỳ nguồn nào vào lúc này, xin lỗi. Nếu ai đó có một số nguồn hiện tại, xin vui lòng chỉnh sửa.


5

Không có "quy tắc" nào như vậy. Một số chương trình lấy đầu vào từ STDIN, và một số thì không. Nếu một chương trình có thể lấy đầu vào từ STDIN, thì nó có thể được dẫn đến, nếu không, nó không thể.

Thông thường bạn có thể biết liệu một chương trình sẽ nhận đầu vào hay không bằng cách suy nghĩ về những gì nó làm. Nếu công việc của chương trình là bằng cách nào đó thao tác các nội dung của một tập tin (ví dụ như grep, sed, awkvv), nó thường có đầu vào từ STDIN. Nếu công việc của mình là để thao tác các tập tin riêng của mình (ví dụ như mv, rm, cp) hoặc một quá trình (ví dụ kill, lsof) hoặc thông tin trở về một cái gì đó (ví dụ top, find, ps) sau đó nó không.

Một cách nghĩ khác về nó là sự khác biệt giữa các đối số và đầu vào. Ví dụ:

mv foo bar

Trong lệnh trên, mvkhông có đầu vào như vậy. Những gì nó đã được đưa ra là hai đối số. Nó không biết hoặc quan tâm những gì trong một trong hai tệp, nó chỉ biết đó là những đối số của nó và nó nên thao túng chúng.

Mặt khác

sed -e 's/foo/bar/' < file
--- -- ------------   ----
 |   |       |          |-> input
 |   |       |------------> argument        
 |   |--------------------> option/flag/switch
 |------------------------> command

Ở đây, sedđã được đưa ra đầu vào cũng như một đối số. Vì nó nhận đầu vào, nó có thể đọc nó từ STDIN và nó có thể được dẫn đến.

Nó trở nên phức tạp hơn khi một đối số có thể đầu vào. Ví dụ

cat file

Đây filelà lý lẽ đã được đưa ra cat. Nói chính xác, tên tập tin filelà đối số. Tuy nhiên, vì catlà một chương trình thao túng nội dung của các tệp, nên đầu vào của nó là bất cứ thứ gì bên trong file.

Điều này có thể được minh họa bằng cách sử dụng strace, một chương trình theo dõi các cuộc gọi hệ thống được thực hiện bởi các quy trình. Nếu chúng tôi chạy cat fooqua strace, chúng tôi có thể thấy rằng tệp foođược mở:

$ strace cat foo 2| grep foo
execve("/bin/cat", ["cat", "foo"], [/* 44 vars */]) = 0
open("foo", O_RDONLY)     

Dòng đầu tiên ở trên cho thấy rằng chương trình /bin/catđã được gọi và các đối số của nó là catfoo(đối số đầu tiên luôn luôn là chính chương trình). Sau đó, đối số foođược mở trong chế độ chỉ đọc. Bây giờ, so sánh điều này với

$ strace ls foo 2| grep foo 
execve("/bin/ls", ["ls", "foo"], [/* 44 vars */]) = 0
stat("foo", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
lstat("foo", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
write(1, "foo\n", 4foo

Ở đây cũng vậy, lslấy chính nó và foolàm đối số. Tuy nhiên, không có opencuộc gọi, đối số không được coi là đầu vào. Thay vào đó, hãy lsgọi statthư viện của hệ thống (không giống với statlệnh) để lấy thông tin về tệp foo.

Tóm lại, nếu lệnh bạn đang chạy sẽ đọc đầu vào của nó, bạn có thể chuyển sang lệnh đó, nếu không, bạn không thể.


0
  • Tại sao nó không hoạt động với kill hoặc rm?

killrmkhông cần STDIN.

  • Có gì khác nhau giữa đầu vào kill, rm với đầu vào grep, awk?

Đối với killrm, người dùng cung cấp thông tin tùy chỉnh của họ làm đối số và $(cmd)giúp lấy STDOUT của cmdvà chuyển đổi đối số thông tin.

Đối với grepawk, người dùng cung cấp các đối số và ngoài ra, STDINhoặc một tệp thông thường sẽ được xử lý bằng lệnh. STDINcó thể được thông qua với đường ống |hoặc bằng cách nhập thủ công.

  • Có quy định nào không?

Đọc hướng dẫn hoặc mã nguồn. Và nếu bạn không tìm thấy gì mình cần, bạn có thể thực hiện một bài kiểm tra đơn giản nhưng có thể nguy hiểm:

Chỉ cần nhập lệnh bạn tò mò, với các đối số mà bạn đã hiểu và xem liệu lệnh tạm dừng (không có gì xảy ra). Nếu nó tạm dừng, nó thực sự đang chờ STDIN (bạn có thể thử catechoxem khác nhau). Bạn nhập thủ công Ctrl-Dvà lệnh đi trước (hiển thị kết quả hoặc lỗi) và trả về. Lệnh như vậy cần STDIN trong tình huống đó (với các đối số bạn cung cấp).

Lệnh tương tự có thể không cần STDIN trong các tình huống khác nhau (ví dụ: catđợi STDIN nhưng cat file.txtkhông).

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.