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?

Martin Vegter
sumber
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 .

fra-san
sumber
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"
Gilles 'SANGAT berhenti menjadi jahat'
sumber
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.

ilkkachu
sumber
0

Bagaimana dengan:

cat "$2" | xz -d | diff /dev/sdtin /dev/stderr 2<<EOT
`cat "$1" | xz -d`
EOT
defdefred
sumber