Mengapa beberapa perintah tidak membaca dari input standar mereka?

19

Saya bertanya-tanya bagaimana kapan kita harus menggunakan pipa dan kapan kita seharusnya tidak.

Katakan misalnya, untuk menghentikan proses tertentu yang menangani file pdf, berikut ini tidak akan berfungsi dengan menggunakan pipa:

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

Sebagai gantinya, kita hanya bisa melakukannya dengan cara berikut:

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

atau

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

Menurut man bash(versi 4.1.2):

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

Untuk skenario di atas:

  • stdin dari grepadalah stdout dari ps. Itu bekerja.
  • stdin dari awkadalah stdout dari grep. Itu bekerja.
  • stdin dari killadalah stdout dari awk. Itu tidak berhasil.

Stdin dari perintah berikut ini selalu mendapatkan input dari stdout perintah sebelumnya.

  • Mengapa itu tidak berhasil killatau rm?
  • Apa yang berbeda antara kill, rminput dengan grep, awkmasukan?
  • Apakah ada aturannya?
Sylye
sumber
1
Ini bukan jawaban tetapi Anda mungkin ingin melihat pada pgrep, pkilldan killallperintah.
terdon
2
@terdon: Saya hanya menggunakan skenario di atas untuk menunjukkan masalah pipa, saya mengerti itu pgrepdan sisanya dapat mencapai ini dengan sempurna :)
sylye

Jawaban:

17

Ada dua cara umum untuk memberikan input ke program:

  • memberikan data ke STDIN dari proses
  • tentukan argumen baris perintah

killhanya menggunakan argumen baris perintah. Itu tidak membaca dari STDIN. Program menyukai grepdan awkmembaca dari STDIN (jika tidak ada nama file yang diberikan sebagai argumen baris perintah) dan memproses data sesuai dengan argumen baris perintah mereka (pola, pernyataan, bendera, ...).

Anda hanya dapat menyalurkan ke STDIN dari proses lain, bukan ke perintah argumen baris.

Aturan umum adalah, bahwa program menggunakan STDIN untuk memproses jumlah data yang berubah-ubah. Semua parameter input tambahan atau, jika biasanya hanya sedikit, dilewatkan oleh argumen baris perintah. Jika baris perintah bisa menjadi sangat panjang, misalnya untuk awkteks program yang panjang , seringkali ada kemungkinan untuk membaca ini dari file program tambahan ( -fopsi awk).

Untuk menggunakan STDOUT program sebagai argumen baris perintah, gunakan $(...)atau jika ada banyak data xargs. findbisa juga ini langsung dengan -exec ... {} +.

Untuk kelengkapan: Untuk menulis argumen baris perintah ke STDOUT, gunakan echo.

Jofel
sumber
1
Bagaimana kita tahu perintah hanya akan mengambil argumen tetapi bukan STDIN? Apakah ada cara yang sistematis atau terprogram daripada menebak atau membaca dari halaman manual? Dengan hanya membaca halaman manual, saya tidak bisa mendapatkan petunjuk khusus untuk memastikan apakah perintah dapat atau tidak dapat mengambil STDIN, karena STDIN juga merupakan bagian dari argumen dari cara halaman buku panduan hadir. Misalnya, gzipdalam SYNOPSIS, tidak dikatakan harus mengambil FILENAME sebagai input. Saya melihat ada cara yang lebih sistematis untuk menentukan itu.
sylye
Ada juga argumen "-" yang berarti "stdin" (atau "stdout") untuk beberapa perintah.
Emmanuel
Tidak akan dengan xargstepat memungkinkan Anda untuk "pipa ke perintah argumen baris"?
T. Verron
@ T. Verron ya, ini adalah tugas xargs. Ini memanggil perintah jika perlu lebih dari sekali (ukuran baris perintah terbatas) dan memiliki banyak opsi lain.
jofel
2
Teks deskripsi akan menjelaskan bagaimana Anda dapat menggunakan program ini. Misalnya, gzip mengatakan: "Program gzip memampatkan dan mendekompresi file menggunakan Lempel-Ziv coding (LZ77). Jika tidak ada file yang ditentukan, gzip akan mengompres dari input standar, atau mendekompres ke output standar." Jika halaman manual tidak menyebutkan input standar, itu tidak akan menggunakannya.
Alan Shutko
16

Ini adalah pertanyaan yang menarik, dan berkaitan dengan bagian dari filosofi Unix / Linux.

Jadi, apa perbedaan antara program-program seperti grep, sed, sortdi satu sisi dan kill, rm, lsdi sisi lain? Saya melihat dua aspek.

The Filter Aspek

  • Jenis program pertama juga disebut filter . Mereka mengambil input, baik dari file atau dari STDIN, memodifikasinya, dan menghasilkan beberapa output, sebagian besar ke STDOUT. Mereka dimaksudkan untuk digunakan dalam pipa dengan program lain sebagai sumber dan tujuan.

  • Program jenis kedua bertindak berdasarkan input, tetapi output yang mereka berikan seringkali tidak terkait dengan input. killtidak memiliki output ketika bekerja secara teratur, juga tidak ls. Hanya memiliki nilai balik untuk menunjukkan kesuksesan. Mereka biasanya tidak mengambil input dari STDIN, tetapi kebanyakan memberikan output ke STDOUT.

Untuk program seperti ls, aspek filter tidak berfungsi dengan baik. Ini tentu saja dapat memiliki input (tetapi tidak memerlukannya), dan output terkait erat dengan input itu, tetapi tidak berfungsi sebagai filter. Namun, untuk program semacam itu, aspek lainnya masih berfungsi:

The semantik Aspek

  • Untuk filter, inputnya tidak memiliki makna semantik . Mereka hanya membaca data, memodifikasi data, mengeluarkan data. Tidak masalah apakah ini daftar nilai numerik, beberapa nama file, atau kode sumber HTML. Arti dari data ini hanya diberikan oleh kode yang Anda berikan ke filter: regex untuk grep, aturan untuk awkatau program Perl.

  • Untuk program lain, suka killatau ls, inputnya memiliki makna , denotasi . killmengharapkan nomor proses, lsmengharapkan nama file atau jalur. Mereka tidak dapat menangani data sewenang-wenang dan mereka tidak dimaksudkan untuk itu. Banyak dari mereka bahkan tidak memerlukan input atau parameter apa pun, seperti ps. Mereka biasanya tidak membaca dari STDIN.

Orang mungkin bisa menggabungkan dua aspek ini: Filter adalah program yang inputnya tidak memiliki makna semantik untuk program tersebut.

Saya yakin saya telah membaca tentang filosofi ini di suatu tempat, tetapi saya tidak ingat sumber apa pun saat ini, maaf. Jika seseorang memiliki beberapa sumber hadir, silakan edit.

Dubu
sumber
5

Tidak ada "aturan" seperti itu. Beberapa program menerima input dari STDIN, dan beberapa lainnya tidak. Jika suatu program dapat mengambil input dari STDIN, itu dapat disalurkan ke, jika tidak, itu tidak bisa.

Anda biasanya dapat mengetahui apakah suatu program akan mengambil input atau tidak dengan memikirkan apa yang dilakukannya. Jika pekerjaan program ini adalah untuk entah bagaimana memanipulasi isi dari file (misalnya grep, sed, awkdll), biasanya membutuhkan waktu masukan dari STDIN. Jika tugasnya adalah untuk memanipulasi file itu sendiri (misalnya mv, rm, cp) atau proses (misalnya kill, lsof) atau untuk informasi kembali tentang sesuatu (misalnya top, find, ps) maka tidak.

Cara lain untuk memikirkannya adalah perbedaan antara argumen dan input. Sebagai contoh:

mv foo bar

Pada perintah di atas, mvtidak memiliki input seperti itu. Apa yang telah diberikan adalah dua argumen. Itu tidak tahu atau peduli apa yang ada di salah satu file, hanya tahu itu adalah argumennya dan itu harus memanipulasi mereka.

Di samping itu

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

Di sini, sedtelah diberikan input sekaligus argumen. Karena membutuhkan input, ia dapat membacanya dari STDIN dan dapat disalurkan ke.

Semakin rumit ketika sebuah argumen bisa menjadi input. Sebagai contoh

cat file

Di sini, fileadalah argumen yang diberikan kepada cat. Lebih tepatnya, nama file fileadalah argumen. Namun, karena catmerupakan program yang memanipulasi isi file, inputnya adalah apa pun yang ada di dalamnya file.

Ini dapat diilustrasikan menggunakan strace, program yang melacak panggilan sistem yang dibuat oleh proses. Jika kita jalankan cat foovia strace, kita dapat melihat bahwa file foodibuka:

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

Baris pertama di atas menunjukkan bahwa program /bin/catdipanggil dan argumennya adalah catdan foo(argumen pertama selalu merupakan program itu sendiri). Kemudian, argumen foodibuka dalam mode hanya baca. Sekarang, bandingkan dengan ini

$ 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

Di sini juga, lsmengambil sendiri dan foosebagai argumen. Namun, tidak ada openpanggilan, argumen tersebut tidak diperlakukan sebagai input. Sebaliknya, lspanggil statpustaka sistem (yang tidak sama dengan statperintah) untuk mendapatkan informasi tentang file tersebut foo.

Singkatnya, jika perintah yang Anda jalankan akan membaca inputnya, Anda bisa mem-pipe ke sana, jika tidak, Anda tidak bisa.

terdon
sumber
0
  • Mengapa itu tidak bekerja dengan kill atau rm?

killdan rmtidak perlu STDIN.

  • Apa perbedaan antara kill, rm input dengan grep, awk input?

Untuk killdan rm, pengguna memberikan informasi khusus mereka sebagai argumen, dan $(cmd)membantu mengambil STDOUT dari cmddan mengonversinya argumen info.

Untuk grepdan awk, pengguna memberikan argumen dan sebagai tambahan, juga STDINfile biasa yang akan diproses oleh perintah. STDINdapat dilewati dengan pipa |atau dengan memasukkan secara manual.

  • Apakah ada aturannya?

Baca manual atau kode sumber. Dan jika Anda tidak menemukan apa pun yang Anda butuhkan, Anda dapat melakukan tes sederhana tetapi mungkin berbahaya:

Masukkan saja perintah yang membuat Anda penasaran, dengan argumen yang sudah Anda pahami, dan lihat apakah perintah itu berhenti (tidak terjadi apa-apa). Jika berhenti, sebenarnya menunggu STDIN (Anda dapat mencoba catdan echomelihat perbedaannya). Anda mengetik secara manual Ctrl-Ddan perintahnya maju (tampilkan hasil atau kesalahan) dan kembali. Perintah seperti itu membutuhkan STDIN dalam situasi itu (dengan argumen yang Anda berikan).

Perintah yang sama mungkin tidak perlu STDIN dalam situasi yang berbeda (misalnya, catmenunggu STDIN tetapi cat file.txttidak).

Alex Huang
sumber