Pertanyaan Apa perbedaan antara "Redirection" dan "Pipe"?


Pertanyaan ini mungkin terdengar agak bodoh, tetapi saya tidak dapat benar-benar melihat perbedaan antara pengalihan dan pipa.

Pengalihan digunakan untuk mengalihkan stdout / stdin / stderr, mis. ls > log.txt.

Pipes digunakan untuk memberikan output dari perintah sebagai input ke perintah lain, mis. ls | grep file.txt.

Tapi mengapa ada dua operator untuk hal yang sama?

Kenapa tidak menulis saja ls > grep untuk melewatkan output melalui, bukankah ini hanya semacam pengalihan juga? Apa yang saya lewatkan?


173
2017-08-07 13:22


asal




Jawaban:


Pipa digunakan untuk melewatkan output ke yang lain program atau utilitas.

Pengalihan digunakan untuk melewatkan output ke salah satu a file atau streaming.

Contoh: thing1 > thing2 vs thing1 | thing2

thing1 > thing2

  1. Shell Anda akan menjalankan program bernama thing1
  2. Semuanya itu thing1 output akan ditempatkan dalam sebuah file bernama thing2. (Catatan - jika thing2 ada, itu akan ditimpa)

Jika Anda ingin melewatkan output dari program thing1 ke sebuah program yang disebut thing2, Anda dapat melakukan hal berikut:

thing1 > temp_file && thing2 < temp_file

yang akan

  1. jalankan program bernama thing1
  2. simpan hasilnya ke dalam file bernama temp_file
  3. jalankan program bernama thing2, berpura-pura bahwa orang di keyboard mengetik konten temp_file sebagai input.

Namun, itu kikuk, jadi mereka membuat pipa sebagai cara yang lebih sederhana untuk melakukan itu. thing1 | thing2 melakukan hal yang sama thing1 > temp_file && thing2 < temp_file

EDIT untuk memberikan detail lebih lanjut untuk dipertanyakan dalam komentar:

Jika > mencoba untuk menjadi "lulus ke program" dan "menulis ke file", itu bisa menyebabkan masalah di kedua arah.

Contoh pertama: Anda mencoba menulis ke file. Sudah ada file dengan nama yang ingin Anda timpa. Namun, file tersebut dapat dieksekusi. Agaknya, itu akan mencoba untuk mengeksekusi file ini, melewati input. Anda harus melakukan sesuatu seperti menulis output ke nama file baru, kemudian mengganti nama file.

Contoh kedua: Seperti yang ditunjukkan oleh Florian Diesch, bagaimana jika ada perintah lain di tempat lain dalam sistem dengan nama yang sama (yang ada di jalur eksekusi). Jika Anda bermaksud membuat file dengan nama itu di folder Anda saat ini, Anda akan terjebak.

Ketiga: jika Anda salah ketik perintah, itu tidak akan memperingatkan Anda bahwa perintah tidak ada. Saat ini, jika Anda mengetik ls | gerp log.txt itu akan memberitahu Anda bash: gerp: command not found. Jika > berarti keduanya, itu hanya akan membuat file baru untuk Anda (lalu memperingatkannya tidak tahu apa yang harus dilakukan dengan log.txt).


194
2017-08-07 13:30



Terima kasih. Anda sebutkan thing1 > temp_file && thing2 < temp_file untuk melakukan lebih mudah dengan pipa. Tapi mengapa tidak menggunakan kembali > operator untuk melakukan ini, mis. thing1 > thing2 untuk perintah thing1 dan thing2 ? Mengapa operator ekstra | ? - John Threepwood
"Ambil output dan tuliskan ke file" adalah tindakan yang berbeda dari "Ambil output dan berikan ke program yang berbeda". Saya akan mengedit lebih banyak pemikiran ke dalam jawaban saya ... - David Oneill
@JohnThreepwood Mereka memiliki arti yang berbeda. Bagaimana jika saya ingin mengarahkan sesuatu ke file bernama less, sebagai contoh? thing | less dan thing > less sangat berbeda, karena mereka melakukan hal yang berbeda. Apa yang Anda usulkan akan menciptakan ambiguitas. - Darkhogg
Apakah akurat untuk mengatakan bahwa "thing1> temp_file" hanyalah gula sintaksis untuk "thing1 | tee temp_file"? Sejak mencari tahu tentang tee, saya hampir tidak pernah menggunakan arahan ulang. - Sridhar-Sarnobat
@ Sridhar-Sarnobat tidak, itu tee perintah melakukan sesuatu yang berbeda. tee menulis keluaran ke kedua layar (stdout) dan berkas. Pengalihan tidak hanya berkas. - David Oneill


Jika maknanya foo > bar akan tergantung pada apakah ada perintah bernama bar yang akan membuat menggunakan redirection jauh lebih sulit dan lebih rentan kesalahan: Setiap kali saya ingin mengarahkan ke file pertama saya harus memeriksa apakah ada perintah yang bernama seperti file tujuan saya.


19
2017-08-07 13:40



Ini akan menjadi masalah hanya jika Anda menulis bar dalam direktori yang merupakan bagian dari Anda $PATH env variabel. Jika Anda berada di sesuatu seperti / bin, maka ot bisa menjadi masalah. Tetapi bahkan kemudian, bar harus memiliki set izin yang dapat dieksekusi, sehingga shell memeriksa tidak hanya untuk mencari yang dapat dieksekusi bar tetapi sebenarnya bisa melaksanakannya. Dan jika yang menjadi perhatian adalah dengan menimpa file yang ada, noclober pilihan shell harus mencegah menimpa file yang ada di redirections. - Sergiy Kolodyazhnyy


Ada perbedaan penting antara kedua operator:

  1. ls > log.txt -> Perintah ini mengirim output ke file log.txt.

  2. ls | grep file.txt -> Perintah ini mengirim output perintah ls ke grep melalui penggunaan pipa (|), dan perintah grep mencari file.txt di dalam input yang diberikan kepadanya oleh perintah sebelumnya.

Jika Anda harus melakukan tugas yang sama menggunakan skenario pertama, maka itu akan menjadi:

ls > log.txt; grep 'file.txt' log.txt

Jadi sebuah pipa (dengan |) digunakan untuk mengirim output ke perintah lain, sedangkan redirection (dengan >) digunakan untuk mengarahkan output ke beberapa file.


11
2017-08-07 13:32





Dari Buku Panduan Administrasi Sistem Unix dan Linux:

Redirection

Shell menafsirkan simbol <,>, dan >> sebagai instruksi untuk mengubah rute a komando masukan atau keluaran ke atau dari mengajukan.

Pipes

Untuk menghubungkan STDOUT satu perintah ke STDIN dari lain gunakan | simbol, umumnya dikenal sebagai pipa.

Jadi interpretasi saya adalah: Jika itu perintah untuk memerintah, gunakan pipa. Jika Anda keluaran ke atau dari file menggunakan redirect.


9
2018-02-16 00:40





Ada perbedaan sintaksis besar antara keduanya:

  1. Pengalihan adalah argumen untuk sebuah program
  2. Sebuah pipa memisahkan dua perintah

Anda dapat memikirkan pengalihan seperti ini: cat [<infile] [>outfile]. Ini berarti pesanan tidak masalah: cat <infile >outfile sama dengan cat >outfile <infile. Anda bahkan dapat mencampur pengalihan dengan argumen lain: cat >outfile <infile -b dan cat <infile -b >outfile keduanya baik-baik saja. Anda juga dapat merangkai bersama lebih dari satu input atau output (input akan dibaca secara berurutan dan semua output akan ditulis ke setiap file output): cat >outfile1 >outfile2 <infile1 <infile2. Target atau sumber pengalihan dapat berupa namafile atau nama aliran (seperti & 1, setidaknya dalam bash).

Tetapi pipa benar-benar memisahkan satu perintah dari perintah lain, Anda tidak dapat mencampurnya dengan argumen:

[command1] | [command2]

Pipa mengambil semua yang ditulis ke output standar dari command1 dan mengirimkannya ke input standar dari command2.

Anda juga dapat menggabungkan perpipaan dan pengalihan. Sebagai contoh:

cat <infile >outfile | cat <infile2 >outfile2

Pertama cat akan membaca garis dari infile, kemudian secara bersamaan menulis setiap baris untuk mem-outfile dan mengirimnya ke yang kedua cat.

Yang kedua cat, masukan standar pertama membaca dari pipa (isi infile), kemudian membaca dari infile2, menulis setiap baris ke outfile2. Setelah menjalankan ini, file akan menjadi salinan infile, dan outfile2 akan berisi infile diikuti oleh infile2.

Akhirnya, Anda benar-benar melakukan sesuatu yang sangat mirip dengan contoh Anda menggunakan pengalihan "di sini string" (bash family only) dan backticks:

grep blah <<<`ls`

akan memberikan hasil yang sama seperti

ls | grep blah

Tapi saya pikir versi redirection pertama akan membaca semua output dari ls ke buffer (dalam memori), dan kemudian memberi makan buffer itu ke grep satu baris pada suatu waktu, sedangkan versi piped akan mengambil setiap baris dari ls ketika muncul, dan meneruskannya ke grep.


3
2017-08-23 22:24



Nitpick: order penting dalam redirection jika Anda mengalihkan satu fd ke yang lain: echo yes 1>&2 2>/tmp/blah; wc -l /tmp/blah; echo yes 2>/tmp/blah 1>&2; wc -l /tmp/blah Selanjutnya, pengalihan ke file hanya akan menggunakan pengalihan terakhir. echo yes >/tmp/blah >/tmp/blah2 hanya akan menulis kepada /tmp/blah2. - muru
Redirect sebenarnya bukan argumen untuk program. Program tidak akan tahu atau peduli di mana outputnya pergi (atau input berasal dari). Ini hanya cara memberitakan cara mengatur berbagai hal sebelum menjalankan program. - Alois Mahdal


Untuk menambah jawaban lainnya, ada juga perbedaan semantik yang halus - mis. pipa lebih mudah ditutup daripada pengalihan:

seq 5 | (head -n1; head -n1)                # just 1
seq 5 > tmp5; (head -n1; head -n1) < tmp5   # 1 and 2
seq 5 | (read LINE; echo $LINE; head -n1)   # 1 and 2

Dalam contoh pertama, saat panggilan pertama ke head selesai, itu menutup pipa, dan seq berakhir, jadi tidak ada masukan yang tersedia untuk yang kedua head.

Dalam contoh kedua, head mengkonsumsi baris pertama, tetapi ketika menutupnya sendiri stdin  pipa, file tetap terbuka untuk panggilan berikutnya untuk digunakan.

Contoh ketiga menunjukkan bahwa jika kita menggunakan read untuk menghindari penutupan pipa itu masih tersedia dalam subproses.

Jadi "aliran" adalah hal yang kita data melalui (stdin dll), dan sama dalam kedua kasus, tetapi pipa menghubungkan aliran dari dua proses, di mana pengalihan menghubungkan aliran antara proses dan file, sehingga Anda dapat melihat sumber dari kedua persamaan dan perbedaan.

P.S. Jika Anda ingin tahu dan / atau terkejut dengan contoh-contoh itu seperti saya, Anda dapat menggali lebih jauh menggunakan trap untuk melihat bagaimana proses menyelesaikan, E.g:

(trap 'echo seq EXITed >&2' EXIT; seq 5) | (trap 'echo all done' EXIT; (trap 'echo first head exited' EXIT; head -n1)
echo '.'
(trap 'echo second head exited' EXIT; head -n1))

Terkadang proses pertama ditutup sebelumnya 1 dicetak, kadang-kadang sesudahnya.

Saya juga merasa menarik untuk digunakan exec <&- untuk menutup aliran dari pengarahan ulang untuk memperkirakan perilaku pipa (meskipun dengan kesalahan):

seq 5 > tmp5
(trap 'echo all done' EXIT
(trap 'echo first head exited' EXIT; head -n1)
echo '.'
exec <&-
(trap 'echo second head exited' EXIT; head -n1)) < tmp5`

2
2018-06-05 00:54



"ketika panggilan pertama untuk menyelesaikan kepala, itu menutup pipa" Ini sebenarnya tidak akurat karena dua alasan. Satu, (head -n1; head -n1) adalah subkulit dengan dua perintah, masing-masing mewarisi ujung pipa dibaca sebagai pendeskripsi 0, dan dengan demikian subkulit DAN setiap perintah memiliki deskriptor file terbuka. Alasan kedua, Anda dapat melihat bahwa dengan strace -f bash -c 'seq 5 | (head -n1; head -n1) '. Jadi kepala pertama hanya menutup salinan deskriptor file - Sergiy Kolodyazhnyy
Contoh ketiga juga tidak akurat, karena read hanya mengkonsumsi baris pertama (yang satu byte untuk 1 dan baris baru). seq dikirim dalam total 10 byte (5 nomor dan 5 baris baru). Jadi ada 8 byte yang tersisa di buffer pipa, dan itulah mengapa kedua head bekerja - ada data yang masih tersedia di buffer pipa. Btw, kepala keluar hanya jika ada bita dibaca, agak seperti dalam head /dev/null - Sergiy Kolodyazhnyy
Terimakasih atas klarifikasinya. Apakah saya memahami dengan benar bahwa dalam seq 5 | (head -n1; head -n1)panggilan pertama mengosongkan pipa, sehingga masih ada dalam keadaan terbuka tetapi tanpa data untuk panggilan kedua head? Jadi perbedaan dalam perilaku antara pipa dan pengalihan adalah karena kepala menarik semua data keluar dari pipa, tetapi hanya 2 baris dari pegangan file? - Julian de Bhal
Itu benar. Dan itu adalah sesuatu yang bisa dilihat strace perintah yang saya berikan di komentar pertama. Dengan pengalihan, file tmp ada di disk yang membuatnya bisa dicari (karena mereka gunakan lseek() syscall - perintah dapat melompati file dari byte pertama ke last namun mereka inginkan. Tetapi pipa bersifat sekuensial dan tidak dapat dicari. Jadi satu-satunya cara bagi kepala untuk melakukan tugasnya adalah membaca semuanya terlebih dahulu, atau jika file tersebut memetakan sebagian besar ke RAM melalui mmap() panggilan. Saya pernah melakukannya sendiri tail dengan Python, dan mengalami masalah yang sama persis. - Sergiy Kolodyazhnyy
Penting juga untuk diingat bahwa membaca ujung pipa (file descriptor) diberikan kepada subkulit pertama (...), dan subkulit akan membuat salinan stdinnya sendiri untuk setiap perintah di dalamnya (...). Jadi mereka secara teknis membaca dari objek yang sama. Pertama head  berpikir itu membaca dari stdin itu sendiri. Kedua head berpikir itu memiliki stdin sendiri. Namun kenyataannya fd # 1 (stdin) mereka hanyalah salinan fd yang sama, yang dibaca ujung pipa. Juga, saya sudah mengirim jawaban, jadi mungkin itu akan membantu memperjelas sesuatu. - Sergiy Kolodyazhnyy


Catatan: Jawabannya mencerminkan pemahaman saya sendiri tentang mekanisme ini terkini, akumulasi penelitian dan pembacaan jawaban oleh rekan-rekan di situs ini dan unix.stackexchange.com, dan akan diperbarui seiring waktu. Jangan ragu untuk bertanya atau menyarankan perbaikan pada komentar. Saya juga menyarankan Anda mencoba untuk melihat bagaimana syscalls bekerja dengan shell strace perintah. Juga tolong jangan terintimidasi oleh gagasan internal atau syscalls - Anda tidak harus tahu atau dapat menggunakannya untuk memahami bagaimana shell melakukan sesuatu, tetapi mereka pasti membantu pemahaman.

TL; DR

  • | pipa tidak terkait dengan entri pada disk, oleh karena itu tidak memiliki inode jumlah sistem file disk (tetapi memiliki inode di pipefs filesystem virtual di kernel-space), tetapi pengalihan sering melibatkan file, yang memang memiliki entri disk dan karena itu memiliki inode yang sesuai.
  • pipa tidak lseek()'sehingga dapat perintah tidak dapat membaca beberapa data dan kemudian mundur kembali, tetapi ketika Anda mengarahkan ulang > atau < biasanya itu adalah file yang lseek() objek mampu, sehingga perintah dapat menavigasi sesuka mereka.
  • pengalihan adalah manipulasi pada deskriptor file, yang bisa banyak; pipa hanya memiliki dua deskriptor file - satu untuk perintah kiri dan satu lagi untuk perintah kanan
  • redirection pada stream standar dan pipa keduanya buffered.
  • pipa hampir selalu melibatkan forking, redirections - tidak selalu
  • pipa selalu berurusan dengan deskriptor file, pengalihan - baik menggunakan file yang sebenarnya dengan nama file pada disk, atau deskriptor file.
  • pipa adalah metode Inter-Process Communication, sedangkan pengalihan hanya manipulasi pada file yang terbuka atau objek seperti file
  • keduanya bekerja dup2() syscalls di bawah kap untuk menyediakan salinan deskriptor file, di mana aliran data yang sebenarnya terjadi.
  • pengalihan dapat diterapkan "secara global" dengan exec built-in command (lihat ini dan ini ), jadi jika Anda melakukannya exec > output.txt setiap perintah akan ditulis output.txt sejak saat itu. | pipa diterapkan hanya untuk perintah saat ini (yang berarti baik perintah sederhana atau subkulit seperti seq 5 | (head -n1; head -n2)atau perintah gabungan.
  • Ketika pengalihan dilakukan pada file, hal-hal seperti echo "TEST" > file dan echo "TEST" >> file keduanya digunakan open() syscall pada file itu (Lihat juga) dan mendapatkan deskripsi file dari itu untuk dibagikan dup2(). Pipes | hanya digunakan pipe() dan dup2() syscall.

pengantar

Untuk memahami bagaimana kedua mekanisme ini berbeda, penting untuk memahami sifat-sifat esensial mereka, sejarah di belakang keduanya, dan akar mereka dalam bahasa pemrograman C. Faktanya, mengetahui apa itu deskriptor file, dan bagaimana caranya dup2() dan pipe() sistem panggilan kerja sangat penting, begitu juga lseek(). Shell dimaksudkan sebagai cara untuk membuat mekanisme ini menjadi abstrak bagi pengguna, tetapi menggali lebih dalam daripada abstraksi membantu memahami sifat sebenarnya dari perilaku shell.

Asal-usul Pengalihan dan Pipa

Menurut artikel Dennis Ritche Petroglyphs kenabian, pipa berasal dari Memo internal 1964 oleh Malcolm Douglas McIlroy, pada saat mereka sedang mengerjakan Sistem operasi Multik. Kutipan:

Untuk menempatkan kekhawatiran saya yang paling kuat secara singkat:

  1. Kita harus memiliki beberapa cara untuk menghubungkan program seperti selang taman - sekrup di segmen lain ketika menjadi ketika perlu memijat data dengan cara lain. Ini adalah cara IO juga.

Yang jelas adalah bahwa pada saat itu program mampu menulis ke disk, namun itu tidak efisien jika outputnya besar. Untuk mengutip penjelasan Brian Kernighan di Pipa Unix video:

Pertama, Anda tidak perlu menulis satu program besar yang besar - Anda sudah memiliki program yang lebih kecil yang mungkin sudah melakukan bagian dari pekerjaan ... Lainnya adalah bahwa mungkin jumlah data yang Anda proses tidak akan cocok jika Anda menyimpannya dalam file ... karena ingat, kita kembali pada hari-hari ketika disk pada hal-hal ini, jika Anda beruntung, Megabyte atau dua data ... Jadi pipa tidak pernah harus memberi contoh seluruh output .

Dengan demikian perbedaan konseptual jelas: pipa adalah mekanisme membuat program berbicara satu sama lain. Redirections - adalah cara penulisan untuk file pada tingkat dasar. Dalam kedua kasus, shell membuat dua hal ini mudah, tetapi di balik kap mesin, ada banyak hal yang terjadi.

Lebih dalam: syscalls dan cara kerja internal shell

Kami mulai dengan gagasan pendeskripsi file. File deskriptor pada dasarnya mendeskripsikan file terbuka (apakah itu file pada disk, atau dalam memori, atau file anonim), yang diwakili oleh bilangan integer. Keduanya aliran data standar  (stdin, stdout, stderr) adalah file descriptor 0,1, dan 2 masing-masing. Mereka berasal dari mana ? Nah, dalam perintah shell pendeskripsi file diwariskan dari induknya - shell. Dan itu benar secara umum untuk semua proses - proses anak mewarisi deskriptor file orang tua. Untuk daemon adalah umum untuk menutup semua deskriptor file yang diwariskan dan / atau mengalihkan ke tempat lain.

Kembali ke pengalihan. Apa itu sebenarnya? Ini adalah mekanisme yang memberitahu shell untuk menyiapkan deskriptor file untuk perintah (karena pengalihan dilakukan oleh shell sebelum perintah dijalankan), dan arahkan mereka di mana pengguna disarankan. Itu definisi standar output redirection adalah

[n]>word

Bahwa [n] ada nomor deskripsi file. Saat kamu melakukan echo "Something" > /dev/null nomor 1 tersirat di sana, dan echo 2> /dev/null.

Di bawah kap ini dilakukan dengan menduplikasi file deskriptor melalui dup2() panggilan sistem. Mari kita ambil df > /dev/null. Shell akan membuat proses anak di mana df berjalan, tetapi sebelum itu akan terbuka /dev/null sebagai file descriptor # 3, dan dup2(3,1) akan dikeluarkan, yang membuat salinan deskriptor file 3 dan salinnya adalah 1. Anda tahu bagaimana Anda memiliki dua file file1.txt dan file2.txt, dan ketika Anda melakukannya cp file1.txt file2.txt Anda akan memiliki dua file yang sama, tetapi Anda dapat memanipulasinya secara independen? Itu agak hal yang sama terjadi di sini. Seringkali Anda dapat melihat bahwa sebelum menjalankan, bash akan melakukan dup(1,10) untuk membuat salinan file deskriptor # 1 yang mana stdout (dan salinan itu akan fd # 10) untuk mengembalikannya nanti. Penting untuk dicatat bahwa ketika Anda mempertimbangkan perintah built-in (yang merupakan bagian dari shell itu sendiri, dan tidak memiliki file di dalamnya /bin atau di tempat lain) atau perintah sederhana di shell non-interaktif, shell tidak membuat proses anak.

Dan kemudian kita memiliki hal-hal seperti itu [n]>&[m] dan [n]&<[m]. Ini menduplikasi file deskriptor, yang mekanisme yang sama seperti dup2() hanya sekarang ini dalam sintaks shell, mudah tersedia untuk pengguna.

Salah satu hal penting yang perlu diperhatikan tentang redirection adalah bahwa pesanan mereka tidak tetap, tetapi signifikan terhadap bagaimana shell menafsirkan apa yang diinginkan pengguna. Bandingkan yang berikut ini:

# Make copy of where fd 2 points , then redirect fd 2
$ ls -l /proc/self/fd/  3>&2  2> /dev/null
total 0
lrwx------ 1 user user 64 Sep 13 00:08 0 -> /dev/pts/0
lrwx------ 1 user user 64 Sep 13 00:08 1 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:08 2 -> /dev/null
lrwx------ 1 runner user 64 Sep 13 00:08 3 -> /dev/pts/0
lr-x------ 1 user user 64 Sep 13 00:08 4 -> /proc/29/fd

# redirect fd #2 first, then clone it
$ ls -l /proc/self/fd/    2> /dev/null 3>&2
total 0
lrwx------ 1 user user 64 Sep 13 00:08 0 -> /dev/pts/0
lrwx------ 1 user user 64 Sep 13 00:08 1 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:08 2 -> /dev/null
l-wx------ 1 user user 64 Sep 13 00:08 3 -> /dev/null
lr-x------ 1 user user 64 Sep 13 00:08 4 -> /proc/31/fd

Penggunaan praktis ini dalam scripting shell dapat serbaguna:

dan banyak lainnya.

Pipa dengan pipe() dan dup2()

Jadi bagaimana cara membuat pipa? Melalui pipe() syscall, yang akan mengambil sebagai input array (alias daftar) yang disebut pipefd dari dua jenis barang int (bilangan bulat). Kedua bilangan bulat tersebut merupakan pendeskripsi file. Itu pipefd[0] akan menjadi ujung baca dari pipa dan pipefd[1] akan menjadi ujung menulis. Jadi dalam df | grep 'foo', grep akan mendapatkan salinan pipefd[0] dan df akan mendapatkan salinannya pipefd[1]. Tapi bagaimana caranya ? Tentu saja, dengan keajaiban dup2() syscall. Untuk df dalam contoh kita, katakanlah pipefd[1] memiliki # 4, jadi cangkang akan membuat anak, lakukan dup2(4,1) (ingat saya cp contoh?), dan kemudian lakukan execve() untuk benar-benar berjalan df. Tentu saja, df akan mewarisi file descriptor # 1, tetapi akan tidak menyadari bahwa itu tidak lagi menunjuk pada terminal, tetapi sebenarnya fd # 4, yang sebenarnya adalah ujung penulisan dari pipa. Tentu saja, hal yang sama akan terjadi grep 'foo' kecuali dengan jumlah deskriptor file yang berbeda.

Sekarang, pertanyaan menarik: bisakah kita membuat pipa yang mengarahkan fd # 2 juga, bukan hanya fd # 1? Ya, sebenarnya begitulah |&tidak di bash. Standar POSIX membutuhkan bahasa perintah shell untuk mendukung df 2>&1 | grep 'foo' sintaks untuk tujuan itu, tapi bash tidak |& demikian juga.

Yang penting untuk dicatat adalah bahwa pipa selalu berhubungan dengan pendeskripsi file. Ada di sana FIFO atau bernama pipa, yang memiliki nama file pada disk dan membiarkan Anda menggunakannya sebagai file, tetapi berperilaku seperti pipa. Tetapi | jenis pipa adalah apa yang dikenal sebagai pipa anonim - mereka tidak memiliki nama file, karena mereka benar-benar hanya dua benda yang terhubung bersama. Fakta bahwa kita tidak berurusan dengan file juga membuat implikasi penting: pipa tidak lseek()'sanggup. File, baik dalam memori atau pada disk, bersifat statis - program dapat digunakan lseek() syscall untuk melompat ke byte 120, lalu kembali ke byte 10, lalu maju terus sampai akhir. Pipa tidak statis - mereka berurutan, dan karena itu Anda tidak dapat memundurkan data yang Anda dapatkan dari mereka lseek(). Inilah yang membuat beberapa program sadar jika mereka membaca dari file atau dari pipa, dan dengan demikian mereka dapat membuat penyesuaian yang diperlukan untuk kinerja yang efisien; dengan kata lain, a prog dapat mendeteksi jika saya melakukannya cat file.txt | prog atau prog < input.txt. Contoh nyata dari hal itu adalah ekor.

Dua lainnya yang sangat menarik dari pipa adalah bahwa mereka memiliki penyangga, yang di Linux adalah 4096 byte, dan mereka sebenarnya punya filesystem sebagaimana didefinisikan dalam kode sumber Linux ! Mereka bukan sekadar objek untuk meneruskan data, mereka adalah datastructure sendiri! Bahkan, karena ada filesystem pipefs, yang mengelola kedua pipa dan FIFO, pipa memiliki inode nomor pada filesystem mereka masing-masing:

# Stdout of ls is wired to pipe
$ ls -l /proc/self/fd/  | cat  
lrwx------ 1 user user 64 Sep 13 00:02 0 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:02 1 -> pipe:[15655630]
lrwx------ 1 user user 64 Sep 13 00:02 2 -> /dev/pts/0
lr-x------ 1 user user 64 Sep 13 00:02 3 -> /proc/22/fd
# stdin of ls is wired to pipe
$ true | ls -l /proc/self/fd/0
lr-x------ 1 user user 64 Sep 13 03:58 /proc/self/fd/0 -> 'pipe:[54741]'

Pada pipa Linux adalah uni-directional, seperti halnya redirection. Pada beberapa implementasi mirip Unix - ada pipa dua arah. Meskipun dengan magic of shell scripting, Anda bisa membuatnya pipa bi-directional di Linux demikian juga.

Lihat juga:


2
2017-09-12 09:26





Saya sudah mendapat masalah dengan ini di C hari ini. Pada dasarnya, Pipa memiliki semantik yang berbeda untuk pengalihan juga, bahkan ketika dikirim ke stdin. Sungguh saya pikir mengingat perbedaannya, pipa harus pergi ke tempat lain selain stdin, maka stdin dan mari kita menyebutnya stdpipe (untuk membuat diferensial yang sewenang-wenang) dapat ditangani dengan cara yang berbeda.

Pertimbangkan ini. Ketika mem-pipe satu output program ke yang lain fstat tampaknya mengembalikan nol sebagai st_size meskipun ls -lha /proc/{PID}/fd menunjukkan bahwa ada file. Ketika mengalihkan file ini tidak terjadi (setidaknya pada debian wheezy, stretch dan jessie vanila dan ubuntu 14.04, 16.04vanila.

Jika kamu cat /proc/{PID}/fd/0 dengan pengarahan ulang Anda akan dapat mengulang untuk membaca sebanyak yang Anda suka. Jika Anda melakukan ini dengan pipa Anda akan melihat bahwa kedua kalinya Anda menjalankan tugas secara berurutan, Anda tidak mendapatkan hasil yang sama.


1
2017-10-26 16:17