Bagaimana cara meniru Proses Substitusi di Dash?


18

Dalam bash, saya dapat menggunakan Proses Pergantian dan memperlakukan output dari suatu proses seolah-olah itu adalah file yang disimpan di disk:

$ echo <(ls)
/dev/fd/63

$ ls -lAhF <(ls)
lr-x------ 1 root root 64 Sep 17 12:55 /dev/fd/63 -> pipe:[1652825]

sayangnya, Proses Pergantian tidak didukung di dash.

Apa cara terbaik untuk meniru Process Substitutiondi dash?

Saya tidak ingin menyimpan output sebagai file sementara di suatu tempat ( /tmp/) dan kemudian harus menghapusnya. Apakah ada cara alternatif?


Tergantung pada apa yang Anda coba lakukan, Anda mungkin dapat menggunakan pipa.
— Eric Renouf

Jujur saja, jika portabilitas bukan perhatian utama Anda di sini bukankah akan lebih mudah untuk hanya menginstal bashpada perangkat Anda?
— Arkadiusz Drabczyk

1
Contoh yang Anda berikan dalam pemberitahuan karunia kebetulan menjadi subjek dari jawaban terkait ini . Seperti yang ditunjukkan di sana, versi jawaban Gilles yang disederhanakan (dengan asumsi ketersediaan /dev/fddan penggunaan xz -cd <file>alih-alih cat <file> | xz -d) bisa jadi xz -cd "$1" | { xz -cd "$2" | { diff /dev/fd/3 /dev/fd/4; } 3<&0; } 4<&0.
— fra-san

1
@ fra-san - itu sebenarnya yang saya butuhkan. Anda bisa menjawabnya jika mau. Terima kasih.
— Martin Vegter

Jawaban:


4

Pertanyaan dalam pemberitahuan karunia saat ini:

contoh umum terlalu rumit. Adakah yang bisa menjelaskan bagaimana menerapkan contoh berikut?diff <(cat "$2" | xz -d) <(cat "$1" | xz -d)

sepertinya punya jawaban di sini .

Seperti yang ditunjukkan dalam jawaban Gilles , ide umumnya adalah untuk mengirim output dari perintah "produser" ke file perangkat baru 1 pada berbagai tahapan pipa, menjadikannya tersedia untuk perintah "konsumen", yang mungkin dapat menggunakan nama file sebagai argumen ( dengan asumsi bahwa sistem Anda memberi Anda akses ke deskriptor file sebagai /dev/fd/X).

Cara paling sederhana untuk mencapai apa yang Anda cari mungkin adalah:

xz -cd file1.xz | { xz -cd file2.xz | diff /dev/fd/3 -; } 3<&0

(Menggunakan file1.xzdi tempat "$1"untuk keterbacaan dan xz -cdbukan cat ... | xz -dkarena satu perintah sudah cukup).

Output dari perintah "produser" pertama xz -cd file1.xz,, disalurkan ke perintah gabungan ( {...}); tetapi, alih-alih dikonsumsi langsung sebagai input standar dari perintah berikutnya, itu digandakan ke file deskriptor 3dan dengan demikian dapat diakses oleh semua yang ada di dalam perintah gabungan sebagai /dev/fd/3. Output dari perintah "produser" kedua xz -cd file2.xz, yang tidak mengkonsumsi input standar atau apapun dari deskriptor file 3, kemudian disalurkan ke perintah "konsumen" diff, yang membaca dari input standar dan dari /dev/fd/3.

Duplikasi pemipaan dan deskriptor file dapat ditambahkan untuk menyediakan file perangkat sebanyak mungkin perintah "produsen", misalnya:

xz -cd file1.xz | { xz -cd file2.xz | { diff /dev/fd/3 /dev/fd/4; } 4<&0; } 3<&0

Meskipun mungkin tidak relevan dalam konteks pertanyaan spesifik Anda, perlu dicatat bahwa:

  1. cmd1 <(cmd2) <(cmd3), cmd2 | { cmd3 | { cmd1 /dev/fd/3 /dev/fd/4; } 4<&0; } 3<&0dan ( cmd2 | ( cmd3 | ( cmd1 /dev/fd/3 /dev/fd/4 ) 4<&0 ) 3<&0 )memiliki efek potensial yang berbeda pada lingkungan eksekusi awal.

  2. Bertentangan dengan apa yang terjadi di cmd1 <(cmd2) <(cmd3), cmd3dan cmd1di cmd2 | { cmd3 | { cmd1 /dev/fd/3 /dev/fd/4; } 4<&0; } 3<&0tidak akan dapat membaca input apa pun dari pengguna. Itu akan membutuhkan penjelas file lebih lanjut. Misalnya, untuk mencocokkan

    diff <(echo foo) <(read var; echo "$var")
    

    Anda akan membutuhkan sesuatu seperti

    { echo foo | { read var 0<&9; echo "$var" | diff /dev/fd/3 -; } 3<&0; } 9<&0
    

1 Lebih banyak tentang mereka dapat ditemukan di U&L, misalnya dalam Memahami / dev dan subdir serta file-nya .


12

Anda dapat mereproduksi apa yang shell lakukan di bawah tenda dengan melakukan pipa ledeng secara manual. Jika sistem Anda memiliki entri, Anda dapat menggunakan pengacakan file descriptor: Anda dapat menerjemahkan/dev/fd/NNN

main_command <(produce_arg1) <(produce_arg2) >(consume_arg3) >(consume_arg4)

untuk

{ produce_arg1 |
  { produce_arg2 |
    { main_command /dev/fd5 /dev/fd6 /dev/fd3 /dev/fd4 </dev/fd/8 >/dev/fd/9; } 5<&0 3>&1 |
    consume_arg3; } 6<&0 4>&1; |
  consume_arg4; } 8<&0 9>&1

Saya telah menunjukkan contoh yang lebih kompleks untuk menggambarkan banyak input dan output. Jika Anda tidak perlu membaca dari input standar, dan satu-satunya alasan Anda menggunakan substitusi proses adalah bahwa perintah tersebut membutuhkan nama file yang eksplisit, Anda cukup menggunakan /dev/stdin:

main_command <(produce_arg1)
produce_arg1 | main_command /dev/stdin

Tanpa , Anda perlu menggunakan pipa bernama . Pipa bernama adalah entri direktori, jadi Anda perlu membuat file sementara di suatu tempat, tetapi file itu hanya nama, itu tidak mengandung data apa pun./dev/fd/NNN

tmp=$(mktemp -d)
mkfifo "$tmp/f1" "$tmp/f2" "$tmp/f3" "$tmp/f4"
produce_arg1 >"$tmp/f1" &
produce_arg2 >"$tmp/f2" &
consume_arg3 <"$tmp/f3" &
consume_arg4 <"$tmp/f4" &
main_command "$tmp/f1" "$tmp/f2" "$tmp/f3" "$tmp/f4"
rm -r "$tmp"

2
</dev/fd/8 >/dev/fd/9tidak setara dengan <&8 >&9di Linux dan harus dihindari di sana.
— StĂ©phane Chazelas

@Gilles - Saya bingung dengan contoh kompleks. Bisa tolong tunjukkan bagaimana untuk meniru: diff <(cat "$2" | xz -d) <(cat "$1" | xz -d)?
— Martin Vegter

3

Adakah yang bisa menjelaskan bagaimana menerapkan contoh berikut?
diff <(cat "$2" | xz -d) <(cat "$1" | xz -d)

Saya pikir pipa bernama lebih mudah untuk grok daripada pengalihan, jadi secara sederhana:

mkfifo p1 p2               # create pipes
cat "$2" | xz -d > p1 &    # start the commands in the background
cat "$1" | xz -d > p2 &    #    with output to the pipes
diff p1 p2                 # run 'diff', reading from the pipes
rm p1 p2                   # remove them at the end

p1dan p2sementara pipa bernama, mereka bisa dinamai apa saja.

Akan lebih pintar untuk membuat pipanya /tmp, misalnya dalam direktori yang ditunjukkan oleh jawaban Gilles , dan perhatikan bahwa Anda tidak perlu di catsini, jadi:

tmpdir=$(mktemp -d)
mkfifo "$tmpdir/p1" "$tmpdir/p2"
xz -d < "$2" > "$tmpdir/p1" &
xz -d < "$1" > "$tmpdir/p2" &
diff "$tmpdir/p1" "$tmpdir/p2"
rm -r "$tmpdir"

(Anda mungkin bisa pergi tanpa tanda kutip juga, karena mktempkemungkinan akan membuat nama file "bagus".)

Solusi pipa memang memiliki peringatan bahwa jika perintah utama ( diff) mati sebelum membaca semua input, proses latar belakang dapat dibiarkan menggantung.


0

Bagaimana dengan:

cat "$2" | xz -d | diff /dev/sdtin /dev/stderr 2<<EOT
`cat "$1" | xz -d`
EOT
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.