Pipa semi-sinkron

11

Asumsikan saya memiliki pipa berikut:

a | b | c | d

Bagaimana saya bisa menunggu selesainya c(atau b) di shatau bash? Ini berarti bahwa skrip ddapat mulai kapan saja (dan tidak perlu ditunggu-tunggu) tetapi membutuhkan hasil lengkap cuntuk bekerja dengan benar.

Case use adalah difftooluntuk gityang membandingkan gambar. Ini dipanggil oleh gitdan perlu memproses inputnya ( a | b | cbagian) dan menampilkan hasil perbandingan ( dbagian). Penelepon akan menghapus input yang diperlukan untuk adan b. Ini berarti bahwa sebelum kembali dari skrip, proses c(atau b) harus diakhiri. Di sisi lain, saya tidak bisa menunggu dkarena ini berarti saya sedang menunggu input pengguna.

Saya tahu saya bisa menulis hasil cke file sementara, atau mungkin menggunakan FIFO di bash. (Namun, tidak yakin apakah FIFO akan membantu.) Apakah mungkin untuk mencapai ini tanpa file sementara sh?

EDIT

Mungkin akan cukup jika saya bisa mengetahui ID proses dari c(atau b) proses dengan cara yang dapat diandalkan. Kemudian seluruh pipa dapat dimulai secara tidak sinkron, dan saya bisa menunggu proses ID. Sesuatu di sepanjang garis

wait $(a | b | { c & [print PID of c] ; } | d)

EDIT ^ 2

Saya telah menemukan solusi, komentar (atau solusi yang masih lebih baik) dipersilakan.

krlmlr
sumber
Maksud Anda, Anda ingin dmulai memproses coutput hanya setelah cselesai? Anda tidak ingin dmulai memproses setiap jalur keluaran saat datang?
terdon
@terdon: Tidak, dgratis untuk memulai kapan saja suka, tetapi charus selesai sebelum saya bisa melanjutkan.
krlmlr
Itu tampaknya kontradiktif dengan diri sendiri, jika dbisa dimulai ketika suka, apa yang sebenarnya Anda tunggu?
terdon
@terdon: Diperluas untuk menampilkan use case.
krlmlr
Jika dtidak menggunakan output cmaka sepertinya tidak masuk akal untuk membuat dbagian dari pipa. Tetapi jika dmemang menggunakan input maka dharus bekerja sebentar pada inputnya setelah membaca semua itu agar pendekatan Anda membuat perbedaan.
Hauke ​​Laging

Jawaban:

6
a | b | { c; [notify];} | d

Pemberitahuan dapat dilakukan misalnya dengan sinyal ke PID yang telah diteruskan dalam variabel lingkungan ( kill -USR1 $EXTPID) atau dengan membuat file ( touch /path/to/file).

Ide lain:

Anda menjalankan proses selanjutnya (yang dapat memulai yang Anda tunggu) dari pipa:

a | b | { c; exec >&-; nextprocess;} | d

atau

a | b | { c; exec >&-; nextprocess &} | d
Hauke ​​Laging
sumber
Terima kasih. Tapi kemudian, membuat file sementara untuk output antara lebih verbose dan lebih mudah diurai (untuk manusia). Juga, bagaimana saya menghindari kondisi balapan dengan sinyal? ... Saya berharap untuk solusi yang lebih bersih, tetapi jika ini adalah cara untuk pergi, maka jadilah itu.
krlmlr
@ krlmlr Kondisi lomba apa?
Hauke ​​Laging
notifydapat dieksekusi sebelum saya memasang perangkap sinyal. Bagaimana saya bisa memastikan untuk tidak melewatkan sinyal itu?
krlmlr
@ krlmlr Anda menghentikan skrip yang memanggil pipeline sampai ia menerima sinyal itu sendiri yang dikirim setelah traptelah ditentukan.
Hauke ​​Laging
Suntingan Anda: Pipa disebut dalam satu lingkaran, di mana saya tidak memiliki kendali. Sayangnya, { c; bg; }atau { c; exit 0; }sepertinya tidak berhasil.
krlmlr
5

Jika saya memahami pertanyaan Anda dengan benar, ini akan berhasil:

a | b | c | { (exec <&3 3<&-; d) &} 3<&0

(Trik fd 3 adalah karena beberapa (kebanyakan) shell mengarahkan stdin ke / dev / null with &).

Stéphane Chazelas
sumber
4

Di bash, Anda dapat menggunakan proses substitusi . Perintah dalam proses substitusi berjalan secara tidak sinkron, tidak ditunggu.

a | b | c > >(d)
Gilles 'SANGAT berhenti menjadi jahat'
sumber
Terima kasih. Ini bashhanya, benar - tidak ada shdukungan?
krlmlr
@ krlmlr Hanya Bash / zsh (dan ksh93 dengan beberapa modifikasi).
Gilles 'SANGAT berhenti menjadi jahat'
3

Inilah yang saya temukan dengan coba-coba, dengan bantuan masukan Hauke:

a | b | { c; kill -PIPE $$; } | d

Setara:

a | b | ( c; kill -PIPE $$; ) | d

(Yang terakhir lebih eksplisit, karena {}akan berjalan dalam subkulit pula jika di dalam pipa.)

Beberapa sinyal lain (termasuk QUIT, TERMdan USR1) berfungsi juga, namun dalam hal ini deskripsi sinyal ditampilkan pada terminal.

Saya ingin tahu apakah ini maksud asli dari PIPEsinyal. Menurut manual :

SIGPIPE: PIPESinyal dikirim ke proses ketika mencoba menulis ke pipa tanpa proses yang terhubung ke ujung lainnya.

Ini berarti bahwa ketika saya secara artifisial mengirim sinyal pipa ke subkulit, itu diam-diam berakhir, meninggalkan konsumen akhir ( d) sendirian.

Ini bekerja di kedua shdan bash.

krlmlr
sumber
0

Anda dapat menggunakan spongeprogram dari paket "moreutils":

a | b | c | sponge | d

spons akan digunakan untuk akhir ckeluaran sebelum disalurkan ke d. Semoga itu yang Anda inginkan.

Minijackson
sumber
0

Anda bisa melakukannya:

a | b | c | (d ; cat > /dev/null)

Jadi, ketika dselesai, catakan menyerap sisa coutput sampai selesai.

Baik. Setelah komentar, saya pikir jawabannya adalah langsung mulai ddi latar belakang.

Melakukan:

a | b | c | (d &)

atau gunakan solusi Stephane Chazelas jika ada masalah dengan dmembaca dari stdin .

angus
sumber
Saya khawatir kasus penggunaan saya agak sebaliknya: cselesai lebih awal dari d, dan saya harus menunggu sampai cselesai tetapi tidak peduli d.
krlmlr
Maaf, saya tidak mengerti. Apa yang ingin Anda lakukan ketika cselesai dan dmasih berjalan? Bunuh d?
angus
dmemiliki GUI dan dapat berjalan hingga pengguna menutupnya.
krlmlr
OKE ... tapi ini sudah terjadi. Ketika a,, bdan cmenyelesaikan pekerjaan mereka, mereka menutup stdout dan keluar; dan hanya dtetap berjalan. Apakah Anda ingin mengirim dke latar belakang saat cselesai? Itu saja?
angus
Ya, dharus dikirim ke latar belakang setelah cselesai.
krlmlr