Pipa digunakan untuk meneruskan output ke program atau utilitas lain .
Redirect digunakan untuk meneruskan output ke file atau stream .
Contoh: thing1 > thing2
vs.thing1 | thing2
thing1 > thing2
- Shell Anda akan menjalankan program bernama
thing1
- Segala sesuatu yang
thing1
keluaran akan ditempatkan di file yang disebut thing2
. (Catatan - jika thing2
ada, itu akan ditimpa)
Jika Anda ingin meneruskan output dari program thing1
ke program yang disebut thing2
, Anda dapat melakukan hal berikut:
thing1 > temp_file && thing2 < temp_file
yang akan
- jalankan program bernama
thing1
- simpan output ke dalam file bernama
temp_file
- menjalankan program bernama
thing2
, berpura-pura bahwa orang di keyboard mengetik konten temp_file
sebagai input.
Namun, itu kikuk, sehingga mereka membuat pipa sebagai cara yang lebih sederhana untuk melakukannya. thing1 | thing2
melakukan hal yang sama denganthing1 > temp_file && thing2 < temp_file
EDIT untuk memberikan rincian lebih lanjut untuk ditanyakan dalam komentar:
Jika >
dicoba menjadi "pass to program" dan "write to file", ini dapat 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, ia akan mencoba untuk mengeksekusi file ini, melewati input. Anda harus melakukan sesuatu seperti menulis keluaran ke nama file baru, lalu mengganti nama file.
Contoh kedua: Seperti yang ditunjukkan Florian Diesch, bagaimana jika ada perintah lain di 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 mandek.
Ketiga: jika Anda salah ketik perintah, itu tidak akan memperingatkan Anda bahwa perintah itu tidak ada. Saat ini, jika Anda mengetiknya ls | gerp log.txt
akan memberi tahu Anda bash: gerp: command not found
. Jika >
maksud keduanya, itu hanya akan membuat file baru untuk Anda (maka peringatkan itu tidak tahu apa yang harus dilakukan dengan log.txt
).
thing1 > temp_file && thing2 < temp_file
untuk lebih mudah menggunakan pipa. Tetapi mengapa tidak menggunakan kembali>
operator untuk melakukan ini, misalnyathing1 > thing2
untuk perintahthing1
danthing2
? Mengapa ada operator tambahan|
?less
, misalnya?thing | less
danthing > less
sangat berbeda, karena mereka melakukan hal yang berbeda. Apa yang Anda usulkan akan menciptakan ambiguitas.tee
perintahnya melakukan sesuatu yang berbeda.tee
menulis output ke layar (stdout
) dan file. Redirect tidak hanya file.Jika maknanya
foo > bar
akan tergantung pada apakah ada perintah bernamabar
yang akan membuat menggunakan pengalihan jauh lebih sulit dan lebih banyak kesalahan cenderung: Setiap kali saya ingin mengarahkan ke file saya pertama-tama harus memeriksa apakah ada perintah bernama seperti file tujuan saya.sumber
bar
di direktori yang merupakan bagian dari$PATH
variabel env Anda . 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 menemukan executablebar
tetapi benar-benar dapat mengeksekusinya. Dan jika masalah terkait dengan menimpa file yang ada,noclober
opsi shell harus mencegah menimpa file yang ada dalam pengalihan.Dari Buku Pegangan Administrasi Sistem Unix dan Linux:
Jadi interpretasi saya adalah: Jika itu perintah ke perintah, gunakan pipa. Jika Anda menghasilkan ke atau dari file, gunakan redirect.
sumber
Ada perbedaan penting antara kedua operator:
ls > log.txt
-> Perintah ini mengirimkan output ke file log.txt.ls | grep file.txt
-> Perintah ini mengirimkan output dari perintah ls to grep melalui penggunaan pipe (|
), dan perintah grep mencari file.txt di input yang disediakan oleh perintah sebelumnya.Jika Anda harus melakukan tugas yang sama menggunakan skenario pertama, maka itu akan menjadi:
Jadi pipa (with
|
) digunakan untuk mengirim output ke perintah lain, sedangkan redirection (with>
) digunakan untuk mengarahkan output ke beberapa file.sumber
Ada perbedaan sintaksis yang besar antara keduanya:
Anda bisa memikirkan pengalihan seperti ini:
cat [<infile] [>outfile]
. Ini menyiratkan pesanan tidak masalah:cat <infile >outfile
sama dengancat >outfile <infile
. Anda bahkan dapat mencampur arahan ulang dengan argumen lain:cat >outfile <infile -b
dancat <infile -b >outfile
keduanya baik-baik saja. Anda juga dapat string bersama lebih dari satu input atau output (input akan dibaca berurutan dan semua output akan ditulis ke setiap file output):cat >outfile1 >outfile2 <infile1 <infile2
. Target atau sumber pengalihan dapat berupa nama file atau nama aliran (seperti & 1, setidaknya dalam bash).Tapi pipa benar-benar memisahkan satu perintah dari perintah lain, Anda tidak dapat mencampurnya dengan argumen:
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:
Yang pertama
cat
akan membaca baris dari infile, lalu secara bersamaan menulis setiap baris ke outfile dan mengirimkannya ke baris keduacat
.Dalam yang kedua
cat
, input standar pertama kali dibaca dari pipa (isi infile), kemudian membaca dari infile2, menulis setiap baris menjadi outfile2. Setelah menjalankan ini, outfile 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" (khusus keluarga) dan backticks:
akan memberikan hasil yang sama dengan
Tapi saya pikir versi pengalihan pertama akan membaca semua output ls menjadi buffer (dalam memori), dan kemudian memberi makan buffer itu untuk menangkap satu baris pada satu waktu, sedangkan versi piped akan mengambil setiap baris dari ls saat muncul, dan melewati garis itu untuk menangkap.
sumber
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/tmp/blah2
.Catatan: Jawabannya mencerminkan pemahaman saya sendiri tentang mekanisme-mekanisme ini terkini, terakumulasi dalam penelitian dan membaca jawaban oleh rekan-rekan di situs ini dan unix.stackexchange.com , dan akan diperbarui seiring berjalannya waktu. Jangan ragu untuk bertanya atau menyarankan peningkatan dalam komentar. Saya juga menyarankan Anda mencoba untuk melihat bagaimana syscalls bekerja di shell dengan
strace
perintah. Juga tolong jangan diintimidasi oleh gagasan internal atau syscalls - Anda tidak perlu tahu atau dapat menggunakannya untuk memahami bagaimana shell melakukan sesuatu, tetapi mereka pasti membantu memahami.TL; DR
|
pipa tidak terkait dengan entri pada disk, oleh karena itu tidak memiliki nomor inode dari sistem file disk (tetapi memiliki inode dalam sistem file virtual pipefs dalam ruang kernel), tetapi pengalihan sering melibatkan file, yang memiliki entri disk dan oleh karena itu telah sesuai inode.lseek()
dapat sehingga perintah tidak dapat membaca beberapa data dan kemudian mundur kembali, tetapi ketika Anda mengarahkan ulang dengan>
atau<
biasanya itu adalah file yanglseek()
dapat objek, jadi perintah dapat menavigasi bagaimanapun mereka silakan.dup2()
syscalls di bawah tenda untuk memberikan salinan deskriptor file, tempat aliran data aktual terjadi.exec
perintah bawaan (lihat ini dan ini ), jadi jika Anda melakukanexec > output.txt
setiap perintah akan menulisoutput.txt
sejak saat itu.|
pipa hanya diterapkan untuk perintah saat ini (yang berarti perintah sederhana atau perintah subkulit sukaseq 5 | (head -n1; head -n2)
atau majemuk.Ketika pengalihan dilakukan pada file, hal-hal seperti
echo "TEST" > file
danecho "TEST" >> file
keduanya menggunakanopen()
syscall pada file itu ( lihat juga ) dan dapatkan deskriptor file darinya untuk diteruskandup2()
. Pipa|
hanya menggunakanpipe()
dandup2()
syscall.Sejauh perintah dieksekusi, pipa dan pengalihan tidak lebih dari deskriptor file - objek seperti file, yang mereka dapat tulis secara membabi buta, atau memanipulasi mereka secara internal (yang dapat menghasilkan perilaku tak terduga;
apt
misalnya, cenderung bahkan tidak menulis ke stdout jika tahu ada pengalihan).pengantar
Untuk memahami bagaimana kedua mekanisme ini berbeda, penting untuk memahami sifat esensial mereka, sejarah di balik keduanya, dan akarnya dalam bahasa pemrograman C. Faktanya, mengetahui apa itu deskriptor file, dan bagaimana
dup2()
danpipe()
system call berfungsi, juga pentinglseek()
. Shell dimaksudkan sebagai cara membuat mekanisme ini abstrak bagi pengguna, tetapi menggali lebih dalam dari abstraksi membantu memahami sifat sebenarnya dari perilaku shell.Asal-usul Pengalihan dan Pipa
Menurut artikel Dennis Ritche Prophetic Petroglyphs , pipa berasal dari memo internal tahun 1964 oleh Malcolm Douglas McIlroy , pada saat mereka sedang mengerjakan sistem operasi Multics . Mengutip:
Yang jelas adalah bahwa pada saat itu program mampu menulis ke disk, namun itu tidak efisien jika outputnya besar. Mengutip penjelasan Brian Kernighan dalam video Unix Pipeline :
Dengan demikian perbedaan konseptual tampak jelas: pipa adalah mekanisme untuk membuat program berbicara satu sama lain. Pengalihan - adalah cara penulisan ke file di tingkat dasar. Dalam kedua kasus, shell membuat kedua hal ini mudah, tetapi di balik tudungnya, ada banyak hal yang terjadi.
Pergi lebih dalam: syscalls dan cara kerja internal shell
Kita mulai dengan gagasan deskriptor file . Deskriptor file pada dasarnya menggambarkan file terbuka (apakah itu file pada disk, atau dalam memori, atau file anonim), yang diwakili oleh angka integer. Dua stream data standar (stdin, stdout, stderr) adalah file deskriptor 0,1, dan 2 masing-masing. Mereka berasal dari mana ? Nah, pada perintah shell, deskriptor file diwarisi dari shell induknya. Dan memang benar secara umum untuk semua proses - proses anak mewarisi deskriptor file orang tua. Untuk daemon biasanya menutup semua deskriptor file yang diwarisi dan / atau mengarahkan ke tempat lain.
Kembali ke pengalihan. Apa itu sebenarnya Ini adalah mekanisme yang memberi tahu shell untuk menyiapkan deskriptor file untuk perintah (karena pengalihan dilakukan oleh shell sebelum perintah dijalankan), dan arahkan mereka ke tempat yang disarankan pengguna. The definisi standar output redirection adalah
Itu
[n]
ada nomor deskriptor file. Ketika Anda melakukanecho "Something" > /dev/null
angka 1 tersirat di sana, danecho 2> /dev/null
.Di bawah tenda ini dilakukan dengan menduplikasi file deskriptor melalui
dup2()
system call. Ayo ambildf > /dev/null
. Shell akan membuat proses anak di manadf
berjalan, tetapi sebelum itu akan terbuka/dev/null
sebagai file deskriptor # 3, dandup2(3,1)
akan dikeluarkan, yang membuat salinan file deskriptor 3 dan salinannya adalah 1. Anda tahu bagaimana Anda memiliki dua filefile1.txt
danfile2.txt
, dan ketika Anda melakukannya,cp file1.txt file2.txt
Anda akan memiliki dua file yang sama, tetapi Anda dapat memanipulasinya secara independen? Hal yang sama terjadi di sini. Seringkali Anda dapat melihat bahwa sebelum menjalankan,bash
akan dilakukandup(1,10)
untuk membuat salinan file descriptor # 1 yangstdout
(dan salinan itu akan menjadi fd # 10) untuk mengembalikannya nanti. Penting untuk dicatat bahwa ketika Anda mempertimbangkan perintah bawaan(yang merupakan bagian dari shell itu sendiri, dan tidak memiliki file di dalam/bin
atau di tempat lain) atau perintah sederhana di shell non-interaktif , shell tidak membuat proses anak.Dan kemudian kita memiliki hal-hal seperti
[n]>&[m]
dan[n]&<[m]
. Ini adalah duplikator deskriptor file, yang mekanisme yang sama sepertidup2()
hanya sekarang ini ada di sintaksis shell, tersedia untuk pengguna.Salah satu hal penting yang perlu diperhatikan tentang pengalihan adalah bahwa pesanan mereka tidak diperbaiki, tetapi penting untuk bagaimana shell menginterpretasikan apa yang diinginkan pengguna. Bandingkan yang berikut ini:
Penggunaan praktis ini dalam skrip shell dapat menjadi serbaguna:
dan banyak lainnya.
Plumbing dengan
pipe()
dandup2()
Jadi bagaimana cara membuat pipa? Melalui
pipe()
syscall , yang akan mengambil input array (alias daftar) yang disebutpipefd
dua item bertipeint
(integer). Kedua bilangan bulat itu adalah deskriptor file. Itupipefd[0]
akan menjadi ujung baca pipa danpipefd[1]
akan menjadi akhir tulis. Jadidf | grep 'foo'
,grep
akan mendapatkan salinanpipefd[0]
dandf
akan mendapatkan salinanpipefd[1]
. Tapi bagaimana caranya ? Tentu saja, dengan keajaibandup2()
syscall. Karenadf
dalam contoh kita, katakanlahpipefd[1]
memiliki # 4, jadi shell akan membuat anak, lakukandup2(4,1)
(ingatcp
contoh saya ?), Dan kemudian lakukanexecve()
untuk benar-benar berjalandf
. Tentu saja,df
akan mewarisi deskriptor file # 1, tetapi tidak akan menyadari bahwa itu tidak lagi menunjuk ke terminal, tetapi sebenarnya fd # 4, yang sebenarnya merupakan akhir dari pipa. Secara alami, hal yang sama akan terjadi dengangrep 'foo'
kecuali dengan jumlah deskriptor file yang berbeda.Sekarang, pertanyaan menarik: bisakah kita membuat pipa yang mengarahkan ulang fd # 2 juga, bukan hanya fd # 1? Ya, sebenarnya itulah yang
|&
dilakukan bash. POSIX standar memerlukan bahasa perintah shell untuk mendukungdf 2>&1 | grep 'foo'
sintaks untuk tujuan itu, tetapibash
tidak|&
juga.Yang penting untuk dicatat adalah bahwa pipa selalu berurusan dengan deskriptor file. Ada pipa bernama
FIFO
atau bernama , yang memiliki nama file pada disk dan mari kita gunakan sebagai file, tetapi berperilaku seperti pipa. Tetapi|
jenis - jenis pipa itu adalah apa yang dikenal sebagai pipa anonim - mereka tidak memiliki nama file, karena mereka benar-benar hanya dua objek yang terhubung bersama. Fakta bahwa kita tidak berurusan dengan file juga membuat implikasi penting: pipa tidaklseek()
bisa. File, baik di memori atau di disk, bersifat statis - program dapat menggunakanlseek()
syscall untuk melompat ke byte 120, lalu kembali ke byte 10, lalu meneruskan semua jalan ke akhir. Pipa tidak statis - mereka berurutan, dan karena itu Anda tidak dapat memundurkan data yang Anda dapatkan darinyalseek()
. 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, aprog
dapat mendeteksi jika saya melakukancat file.txt | prog
atauprog < input.txt
. Contoh nyata dari pekerjaan itu adalah ekor .Dua properti pipa yang sangat menarik lainnya adalah mereka memiliki buffer, yang pada Linux 4096 bytes , dan mereka sebenarnya memiliki sistem file seperti yang didefinisikan dalam kode sumber Linux ! Mereka bukan hanya objek untuk mengirimkan data, mereka adalah struktur data sendiri! Faktanya, karena terdapat filesystem pipefs, yang mengelola pipa dan FIFO, pipa memiliki nomor inode pada sistem file masing-masing:
Di Linux, pipa bersifat uni-directional, seperti halnya pengalihan. Pada beberapa implementasi mirip Unix - ada pipa dua arah. Meskipun dengan keajaiban skrip shell, Anda dapat membuat pipa dua arah di Linux juga.
Lihat juga:
pipe()
syscall dandup2()
.<<
,<<<
diimplementasikan sebagai file temp anonim (tidak terhubung) dibash
danksh
, saat< <()
menggunakan pipa anonim;/bin/dash
menggunakan pipa untuk<<
. Lihat Apa perbedaan antara <<, <<< dan <<dalam bash?sumber
Untuk menambah jawaban lain, ada perbedaan semantik yang halus juga - mis. Pipa ditutup lebih mudah daripada pengalihan:
Pada contoh pertama, ketika panggilan pertama
head
selesai, ia menutup pipa, danseq
berakhir, jadi tidak ada input yang tersedia untuk yang keduahead
.Dalam contoh kedua, head mengkonsumsi baris pertama, tetapi ketika itu menutup sendiri
stdin
pipanya , file tetap terbuka untuk panggilan berikutnya untuk digunakan.Contoh ketiga menunjukkan bahwa jika kita gunakan
read
untuk menghindari menutup pipa itu masih tersedia dalam subproses.Jadi "aliran" adalah hal yang kita singkirkan data (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 persamaan dan perbedaan.
PS Jika Anda penasaran dan / atau terkejut dengan contoh-contoh itu seperti saya, Anda bisa menggali lebih dalam menggunakan
trap
untuk melihat bagaimana proses menyelesaikan, Misalnya:Terkadang proses pertama ditutup sebelum
1
dicetak, kadang-kadang sesudahnya.Saya juga menemukan itu menarik untuk digunakan
exec <&-
untuk menutup aliran dari pengalihan untuk memperkirakan perilaku pipa (meskipun dengan kesalahan):sumber
read
hanya mengkonsumsi baris pertama (yaitu satu byte untuk1
dan baris baru).seq
mengirim total 10 byte (5 angka dan 5 baris baru). Jadi ada 8 byte yang tersisa di buffer pipa, dan itulah sebabnya keduahead
berfungsi - ada data yang masih tersedia di buffer pipa. Btw, kepala keluar hanya jika ada 0 byte dibaca, agak seperti dihead /dev/null
seq 5 | (head -n1; head -n1)
panggilan pertama mengosongkan pipa, sehingga masih ada dalam keadaan terbuka tetapi tanpa data untuk panggilan keduahead
? Jadi perbedaan perilaku antara pipa dan pengalihan adalah karena kepala menarik semua data dari pipa, tetapi hanya 2 baris yang keluar dari pegangan file?strace
perintah yang saya berikan di komentar pertama. Dengan redirection, file tmp ada pada disk yang membuatnya dapat dicari (karena mereka menggunakanlseek()
syscall - perintah dapat melompati file dari byte pertama hingga yang terakhir yang mereka inginkan. Tetapi pipa berurutan dan tidak dapat dicari. Jadi satu-satunya cara bagi kepala untuk melakukan nya tugasnya adalah untuk membaca semuanya terlebih dahulu, atau jika file besar - petakan beberapa ke RAM melaluimmap()
panggilan. Saya pernah melakukannya sendiritail
dengan Python, dan mengalami masalah yang sama persis.(...)
, dan subshell akan membuat salinan stdin sendiri untuk setiap perintah di dalamnya(...)
. Jadi mereka secara teknis membaca dari objek yang sama. Pertamahead
berpikir itu membaca dari stdin itu sendiri. Keduahead
mengira ia memiliki stdin sendiri. Tetapi pada kenyataannya fd # 1 (stdin) mereka hanyalah salinan dari fd yang sama, yang dibaca ujung pipa. Juga, saya sudah mengirim jawaban, jadi mungkin itu akan membantu memperjelas beberapa hal.Saya mengalami masalah dengan ini di C hari ini. Pada dasarnya Pipe memiliki semantik berbeda untuk dialihkan juga, bahkan ketika dikirim ke
stdin
. Sungguh saya pikir mengingat perbedaan, pipa harus pergi ke tempat lain selainstdin
, sehinggastdin
dan sebut sajastdpipe
(untuk membuat diferensial sewenang-wenang) dapat ditangani dengan cara yang berbeda.Pertimbangkan ini. Ketika piping satu output program ke yang lain
fstat
tampaknya mengembalikan nol sebagaist_size
meskipunls -lha /proc/{PID}/fd
menunjukkan bahwa ada file. Saat mengarahkan file, ini bukan masalahnya (setidaknya pada debianwheezy
,stretch
danjessie
vanilla dan ubuntu14.04
,16.04
vanilla.Jika Anda
cat /proc/{PID}/fd/0
dengan pengalihan Anda akan dapat mengulang membaca sebanyak yang Anda suka. Jika Anda melakukan ini dengan sebuah pipa Anda akan melihat bahwa kedua kalinya Anda menjalankan tugas secara berurutan, Anda tidak mendapatkan hasil yang sama.sumber