Bagaimana cara membuat pipa menunggu akhir file atau berhenti setelah kesalahan?


12

Saya mencoba perintah berikut setelah menonton video ini di shenanigans pipa.

man -k . | dmenu -l 20 | awk '{print $1}' | xargs -r man -Tpdf | zathura -

Ini pada dasarnya mencetak daftar halaman manual ke dmenu agar pengguna dapat memilih salah satunya, kemudian menggunakan xargs untuk menjalankan man -Tpdf %(mencetak untuk stdout pdf dari manpage git dari input xargs ') dan meneruskan pdf ke pembaca pdf (zathura ).

Masalahnya adalah (seperti yang Anda lihat dalam video) pembaca pdf dimulai bahkan sebelum saya memilih satu halaman manual di dmenu. Dan jika saya mengklik Esc dan tidak memilih, pembaca pdf masih terbuka tanpa menunjukkan dokumen sama sekali.

Bagaimana saya bisa membuat pembaca pdf (dan perintah lain dalam rantai pipa) hanya berjalan ketika inputnya mencapai akhir file atau ketika menerima input sama sekali? Atau, sebagai alternatif, bagaimana saya bisa membuat rantai pipa berhenti setelah salah satu perintah berantai mengembalikan status keluar yang tidak nol (sehingga jika dmenu mengembalikan kesalahan karena tidak memilih opsi, perintah berikut tidak dijalankan)?


1
Shell apa yang Anda gunakan? Apakah ini bash?
terdon

Saya sudah mencobanya di bash, zsh, dan sh. Mereka semua memiliki perilaku yang sama.
Seninha

2
Ya, perilakunya standar, saya bertanya shell mana karena pipefailopsi bash yang disebutkan dalam jawaban Kusalandanda.
terdon

Jawaban:


12

Bagaimana saya bisa membuat pembaca pdf (dan perintah lain dalam rantai pipa) hanya berjalan ketika inputnya mencapai akhir file atau ketika menerima input sama sekali?

Ada ifne(di Debian ada dalam moreutilspaket):

ifne menjalankan perintah berikut jika dan hanya jika input standar tidak kosong.

Dalam kasus Anda:

 | ifne zathura -

Terima kasih atas jawabannya, saya tidak tahu perintah ini! Perintah ini (dan yang lainnya dalam moreutils) seharusnya berada di Unix asli dan ditentukan oleh posix ... Ini adalah alat dasar dan Unix-ish ...
Seninha

@ Senin Kesederhanaan ifnesedikit menipu. Unix tidak memiliki operasi "peek pipa", jadi ifneharus benar-benar membaca setidaknya satu byte sebelum memutuskan untuk menjalankan perintah dependen. Itu berarti tidak bisa hanya melakukan tes dan mengeksekusi perintah, tetapi harus membuat pipa lain , melakukan proses lain untuk menjalankan perintah dependen, dan menyalin seluruh aliran dari pipa stdin ke pipa hilir. Jika kasus "input kosong" tidak umum, ifnedapat dengan mudah menghabiskan lebih banyak sumber daya daripada menghemat, rata-rata.

@ Wumpus.Q.Wumbley itu adalah mitos - Anda tidak perlu membaca byte apa pun untuk menentukan apakah ada data dalam pipa. Lihat di sini . Dan di Linux Anda sebenarnya bisa mengintip data dari sebuah pipa (yaitu membaca data tanpa menghapusnya). Saya telah menyebutkan ini dan lebih banyak lagi di komentar untuk jawaban "kanonik" di sini, tetapi mereka dihapus oleh mod karena mereka mungkin merasa bahwa fakta-fakta itu seperti mengurangi dari kedahsyatan jawaban.
Mosvy

6

File pdf seharusnya dapat dicari; pemirsa pdf harus melihat trailer terlebih dahulu dan dari sana lompat ke offset dari tabel xref.

Karena pipa tidak dapat dicari, zathuramenggunakan trik yang membingungkan, di mana itu menyalin semua input ke file sementara, dan kemudian menggunakan file sementara itu seperti biasa. Trik "pintar" semacam ini menciptakan harapan palsu dan mengarahkan orang untuk menganggap bahwa file pdf dapat di-streamable.

Tapi bagaimanapun, zathurabenar - benar menunggu EOF sebelum menampilkan dokumen, Anda tidak perlu melakukan apa pun untuk itu terjadi:

(sleep 10; cat file.pdf) | zathura -
# will really show the content of file.pdf after 10 seconds

Masalahnya adalah bahwa zathuratidak memiliki opsi untuk hanya membuka jendela jika file OK, dan keluar dengan kesalahan jika itu tidak terjadi - itu hanya akan tetap ada seolah-olah semuanya OK:

$ dd if=file.pdf bs=50000 count=1 status=none | zathura -
error: could not open document  # its window still hanging around showing nothing

$ echo $?
0  # really?

Jadi, bahkan jika Anda mengarahkan sendiri output ke file sementara, dan hanya berjalan zathurajika semuanya baik-baik saja, tidak ada jaminan bahwa pengguna tidak akan dilayani dengan jendela hitam jika zathuratidak suka output untuk satu alasan atau yang lain .


Btw,

man -X man

akan menampilkan halaman manual di jendela X11 dengan gxditview, bahkan jika itu terlihat langsung dari '70 ;-)

Dan, tentu saja, Anda selalu dapat menggunakan:

... | xargs xterm -e man

yang, di samping banyak perangkat tambahan lainnya, akan memungkinkan Anda menggunakan ekspresi reguler dalam pencarian dan pemilihan teks yang tepat.


6

Semua perintah dalam sebuah pipa mulai cukup banyak pada saat yang bersamaan. Hanya I / O di atas pipa yang menyinkronkannya. Selain itu, pipa hanya dapat menampung informasi sebanyak yang dimungkinkan oleh penyangga pipa.

Karena itu Anda tidak dapat menghindari menjalankan satu tahap pipa, karena

  1. perintah pada tahap itu dimulai segera setelah semua tahapan lainnya dimulai, dan
  2. jika perintah tidak mengkonsumsi input yang masuk melewati pipa, itu akan memblokir tahap-tahap sebelumnya dari pipa.

Sebagai gantinya, tulis output ke file sambil membiarkan pipeline selesai. Kemudian gunakan file itu.

Contoh (sebagai fungsi mengambil satu argumen):

myman () {
    tmpfile=$( mktemp )

    if man -k "$1" | dmenu -l 20 | awk '{print $1}' | xargs -r man -Tpdf >"$tmpfile" && [ -s  "$tmpfile" ]
    then
        zathura "$tmpfile"
    fi

    rm -f "$tmpfile"
}

Ini juga tidak akan menjalankan zathuraprogram jika pipa gagal ( xargsbagian yang dikembalikan tidak nol) atau file yang dihasilkan kosong.

Di dalam bashshell, Anda juga bisa mengatur pipefailopsi shell set -o pipefailagar pipeline mengembalikan status exit dari perintah pertama di pipeline yang gagal. Dan Anda ingin membuat tmpfilevariabel local:

myman () {
    local tmpfile=$( mktemp )

    if [ -o pipefail ]; then
        set -o pipefail
        trap 'set +o pipefail' RETURN
    fi

    if man -k "$1" | dmenu -l 20 | awk '{print $1}' | xargs -r man -Tpdf >"$tmpfile"
    then
        zathura "$tmpfile"
    fi

    rm -f "$tmpfile"
}

Ini menetapkan pipefailopsi untuk durasi fungsi, jika belum ditetapkan, dan kemudian menghapusnya jika diperlukan. Itu menghilangkan -stes pada file output.


1
Mengapa rm -f? Apakah Anda memikirkan kasus di mana pipa mengubah izin tmpfile?
terdon

2
@terdon Saya berpikir tentang kasus di mana file sementara dihapus sebelum waktunya. rm -ftidak akan kesalahan jika file sudah dihapus (mungkin oleh zathura, saya tidak tahu).
Kusalananda

Fungsi pertama tidak berfungsi seperti yang diharapkan: Ini juga akan membuat zathura menampilkan jendela hitam, tapi sekarang zathura berjalan setelah pipa selesai, alih-alih berjalan di samping pipa. Ini karena pipeline mengembalikan status exit xargs, yaitu 0. Perintah yang gagal dalam pipeline adalah dmenu (yang mengembalikan 1 ketika saya memilih apa-apa). Fungsi bash dengan pipefailopsi berfungsi seperti yang diharapkan (dan juga di zsh, yang memiliki opsi yang sama).
Seninha

1
@Seninha Saya memperbaiki fungsi pertama dengan membiarkannya memeriksa apakah file yang dihasilkan tidak kosong.
Kusalananda
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.