it-swarm-id.com

Proses penggantian dan pipa

Saya bertanya-tanya bagaimana memahami hal berikut:

Memipiskan stdout dari perintah ke stdin yang lain adalah teknik yang kuat. Tapi, bagaimana jika Anda perlu mengirim stdout ke beberapa perintah? Di sinilah proses substitusi masuk.

Dengan kata lain, dapatkah proses substitusi melakukan apa pun yang dapat dilakukan pipa?

Apa yang bisa dilakukan proses substitusi, tetapi pipa tidak bisa?

89
Tim

Cara yang baik untuk menemukan perbedaan di antara mereka adalah dengan melakukan sedikit percobaan pada baris perintah. Terlepas dari kesamaan visual dalam penggunaan < karakter, ia melakukan sesuatu yang sangat berbeda dari pengalihan atau pipa.

Mari kita gunakan perintah date untuk pengujian.

$ date | cat
Thu Jul 21 12:39:18 EEST 2011

Ini adalah contoh yang tidak berguna tetapi ini menunjukkan bahwa cat menerima output dari date pada STDIN dan memuntahkannya kembali. Hasil yang sama dapat dicapai dengan proses substitusi:

$ cat <(date)
Thu Jul 21 12:40:53 EEST 2011

Namun apa yang baru saja terjadi di belakang layar berbeda. Alih-alih diberi aliran STDIN, cat sebenarnya melewati nama file yang harus dibuka dan dibaca. Anda dapat melihat langkah ini dengan menggunakan echo alih-alih cat.

$ echo <(date)
/proc/self/fd/11

Ketika kucing menerima nama file, itu membaca konten file untuk kami. Di sisi lain, gema hanya menunjukkan kepada kita nama file yang dilewati. Perbedaan ini menjadi lebih jelas jika Anda menambahkan lebih banyak pergantian:

$ cat <(date) <(date) <(date)
Thu Jul 21 12:44:45 EEST 2011
Thu Jul 21 12:44:45 EEST 2011
Thu Jul 21 12:44:45 EEST 2011

$ echo <(date) <(date) <(date)
/proc/self/fd/11 /proc/self/fd/12 /proc/self/fd/13

Dimungkinkan untuk menggabungkan proses substitusi (yang menghasilkan file) dan pengalihan input (yang menghubungkan file ke STDIN):

$ cat < <(date)
Thu Jul 21 12:46:22 EEST 2011

Itu terlihat hampir sama tetapi kali ini kucing dilewatkan aliran STDIN bukan nama file. Anda dapat melihat ini dengan mencobanya dengan gema:

$ echo < <(date)
<blank>

Karena gema tidak membaca STDIN dan tidak ada argumen yang disampaikan, kami tidak mendapatkan apa-apa.

Pipa dan pengalihan input mendorong konten ke aliran STDIN. Substitusi proses menjalankan perintah, menyimpan outputnya ke file sementara khusus dan kemudian meneruskan nama file itu sebagai pengganti perintah. Perintah apa pun yang Anda gunakan memperlakukannya sebagai nama file. Perhatikan bahwa file yang dibuat bukan file biasa tetapi pipa bernama yang dihapus secara otomatis setelah tidak lagi diperlukan.

140
Caleb

Berikut adalah tiga hal yang dapat Anda lakukan dengan proses substitusi yang tidak mungkin dilakukan sebaliknya.

Beberapa input proses

diff <(cd /foo/bar/; ls) <(cd /foo/baz; ls)

Tidak ada cara untuk melakukan ini dengan pipa.

Mempertahankan STDIN

Katakanlah Anda memiliki yang berikut ini:

curl -o - http://example.com/script.sh
   #/bin/bash
   read LINE
   echo "You said ${LINE}!"

Dan Anda ingin menjalankannya secara langsung. Berikut ini gagal total. Bash sudah menggunakan STDIN untuk membaca skrip, jadi input lain tidak mungkin.

curl -o - http://example.com/script.sh | bash 

Tetapi cara ini bekerja dengan sempurna.

bash <(curl -o - http://example.com/script.sh)

Substitusi proses keluar

Perhatikan juga bahwa proses substitusi juga berfungsi sebaliknya. Jadi Anda dapat melakukan sesuatu seperti ini:

(ls /proc/*/exe >/dev/null) 2> >(sed -n \
  '/Permission denied/ s/.*\(\/proc.*\):.*/\1/p' > denied.txt )

Itu sedikit contoh yang berbelit-belit, tetapi mengirimkan stdout ke /dev/null, ketika pemipaan stderr ke skrip sed untuk mengekstrak nama-nama file yang kesalahannya "Izin ditolak" ditampilkan, dan kemudian mengirim MEREKA hasil ke file.

Perhatikan bahwa perintah pertama dan stdout redirection ada dalam tanda kurung (subshell) sehingga hanya hasil dari perintah THAT yang dikirim ke /dev/null dan itu tidak mengacaukan dengan sisa baris.

26
tylerl

Saya kira Anda berbicara tentang bash atau Shell canggih lainnya, karena posix Shell tidak memiliki proses substitusi.

bash laporan halaman manual:

Substitusi proses
Substitusi proses didukung pada sistem yang mendukung pipa bernama (FIFO) atau metode/dev/fd untuk penamaan file yang terbuka. Ini mengambil bentuk <(daftar) atau> (daftar). Daftar proses dijalankan dengan input atau output yang terhubung ke FIFO atau beberapa file di/dev/fd. Nama file ini diteruskan sebagai argumen ke perintah saat ini sebagai hasil dari Jika bentuk> (daftar) digunakan, menulis ke file akan memberikan input untuk daftar. Jika formulir <(daftar) digunakan, file yang dikirimkan sebagai argumen harus dibaca untuk mendapatkan output dari daftar.

Jika tersedia, substitusi proses dilakukan bersamaan dengan ekspansi parameter dan variabel, substitusi perintah, dan ekspansi aritmatika.

Dengan kata lain, dan dari sudut pandang praktis, Anda dapat menggunakan ekspresi seperti berikut ini

<(commands)

sebagai nama file untuk perintah lain yang membutuhkan file sebagai parameter. Atau Anda dapat menggunakan pengalihan untuk file seperti itu:

while read line; do something; done < <(commands)

Kembali ke pertanyaan Anda, menurut saya proses substitusi dan pipa tidak memiliki banyak kesamaan.

Jika Anda ingin memasang secara berurutan output dari beberapa perintah, Anda dapat menggunakan salah satu dari formulir berikut:

(command1; command2) | command3
{ command1; command2; } | command3

tetapi Anda juga dapat menggunakan pengalihan pada substitusi proses

command3 < <(command1; command2)

akhirnya, jika command3 menerima parameter file (sebagai pengganti stdin)

command3 <(command1; command2)
26
enzotib

Jika suatu perintah mengambil daftar file sebagai argumen dan memproses file-file itu sebagai input (atau output, tetapi tidak umum), masing-masing file tersebut dapat berupa pipa bernama atau/dev/fd file semu yang disediakan secara transparan oleh proses subtitusi:

$ sort -m <(command1) <(command2) <(command3)

Ini akan "mem-pipe" output dari tiga perintah untuk disortir, karena sort dapat mengambil daftar file input pada baris perintah.

10
camh

Perlu dicatat bahwa proses substitusi tidak terbatas pada bentuk <(command), yang menggunakan output command sebagai file. Itu bisa dalam bentuk >(command) yang mengumpankan file sebagai input ke command juga. Ini juga disebutkan dalam kutipan manual bash dalam jawaban @ enzotib.

Untuk contoh date | cat Di atas, perintah yang menggunakan proses substitusi dari bentuk >(command) untuk mencapai efek yang sama adalah,

date > >(cat)

Perhatikan bahwa > Sebelum >(cat) diperlukan. Lagi-lagi ini dapat diilustrasikan dengan jelas oleh echo seperti pada jawaban @ Caleb.

$ echo >(cat)
/dev/fd/63

Jadi, tanpa tambahan >, date >(cat) akan sama dengan date /dev/fd/63 Yang akan mencetak pesan ke stderr.

Misalkan Anda memiliki program yang hanya menggunakan nama file sebagai parameter dan tidak memproses stdin atau stdout. Saya akan menggunakan skrip yang disederhanakan psub.sh Untuk menggambarkan hal ini. Konten psub.sh Adalah

#!/bin/bash
[ -e "$1" -a -e "$2" ] && awk '{print $1}' "$1" > "$2"

Pada dasarnya, ini menguji bahwa kedua argumennya adalah file (tidak harus file biasa) dan jika ini masalahnya, tulislah bidang pertama dari setiap baris "$1" Ke "$2" Menggunakan awk. Kemudian, perintah yang menggabungkan semua yang disebutkan sejauh ini adalah,

./psub.sh <(printf "a a\nc c\nb b") >(sort)

Ini akan dicetak

a
b
c

dan setara dengan

printf "a a\nc c\nb b" | awk '{print $1}' | sort

tetapi yang berikut tidak akan berfungsi, dan kami harus menggunakan proses substitusi di sini,

printf "a a\nc c\nb b" | ./psub.sh | sort

atau bentuknya yang setara

printf "a a\nc c\nb b" | ./psub.sh /dev/stdin /dev/stdout | sort

Jika ./psub.sh Juga membaca stdin selain apa yang disebutkan di atas, maka bentuk yang sepadan seperti itu tidak ada, dan dalam hal itu tidak ada yang dapat kami gunakan sebagai pengganti proses substitusi (tentu saja Anda dapat juga menggunakan pipa bernama atau file temp, tapi itu cerita lain).

3
Weijun Zhou