Як змусити конвеєр очікувати закінчення файлу або зупинити його після помилки?


12

Я спробував наступну команду після перегляду цього відео на трубах шнаніганів.

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

В основному він друкує список manpages для dmenu, щоб користувач вибрав один з них, потім він використовує xargs для запуску man -Tpdf %(print для stdout pdf manit git із введення xargs) і передає pdf у читач pdf (zathura ).

Проблема полягає в тому, що (як ви бачите на відео) зчитувач pdf запускається ще до того, як я виберу одну сторінку в dmenu. Якщо я натискаю клавішу Esc і не вибираю жодну, програма для читання PDF-файлів все ще відкрита, не показуючи жодного документа.

Як я можу змусити зчитувач pdf (та будь-яку іншу команду в ланцюзі труб) запускатись лише тоді, коли його вхід доходить до кінця файлу або коли він взагалі отримує вхід? Або, як варіант, як я можу змусити ланцюг труб зупинитися після того, як одна з ланцюгових команд поверне ненульовий статус виходу (так що, якщо dmenu повертає помилку за не вибір параметра, наступні команди не виконуються)?


1
Яку оболонку ви використовуєте? Це баш?
тердон

Я спробував це на bash, zsh та sh. Усі вони мали однакову поведінку.
Seninha

2
Так, поведінка є стандартною, я запитав, яка оболонка через pipefailваріант Баша, згаданий у відповіді Кусаландданда.
terdon

Відповіді:


12

Як я можу змусити зчитувач PDF (та будь-яку іншу команду в ланцюзі труб) запускатись лише тоді, коли його вхід доходить до кінця файлу або коли він взагалі отримує вхід?

Є ifne(у Debian це в moreutilsпакеті):

ifne виконує наступну команду, якщо і лише якщо стандартний ввід не порожній.

У вашому випадку:

 | ifne zathura -

Дякую за відповідь, я не знав цієї команди! Ця команда (та інші в moreutils) повинні були бути в оригіналі Unix і вказані позисом ... Це такий основний і інструмент Unix-ish ...
Seninha

@Seninha Простота ifneтрохи оманлива. У Unix немає операції "peek peek", тому ifneпотрібно фактично прочитати хоча б один байт, перш ніж вирішити запустити залежну команду. Це означає, що він не може просто виконати тест і виконати команду, але повинен створити іншу трубу, розпалити інший процес для запуску залежної команди та скопіювати весь потік з трубопроводу stdin у нижню трубу. Якщо випадок "порожній вхід" не є загальним, він ifneможе в середньому легко коштувати більше ресурсів, ніж економить.

@ Wumpus.Q.Wumbley - це міф - вам не потрібно читати жоден байт, щоб визначити, чи є дані в трубі. Дивіться тут . А в Linux ви можете фактично зазирати дані з труби (тобто читати дані, не видаляючи їх). Я згадував про це та інше у коментарях до "канонічної" відповіді тут, але вони були видалені модами, тому що вони, ймовірно, відчували, що ці факти неначе приваблюють чудову відповідь.
mosvy

6

Файли PDF повинні бути доступними для пошуку; будь-який переглядач PDF повинен спершу подивитися трейлер, а звідти перейти до компенсацій із таблиці xref.

Оскільки труби не є доступними, zathuraвикористовується обтяжливий трюк, де він копіює весь вхід у тимчасовий файл, а потім використовує цей тимчасовий файл, як зазвичай. Цей "розумний" трюк породжує помилкові надії і спонукає людей припускати, що файли pdf є потоковими.

Але в будь-якому випадку, на zathuraсамому ділі робить очікування для EOF до відображення документа, ви не повинні робити нічого, щоб це hapen:

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

Проблема полягає в тому, що zathuraнемає можливості відкривати вікно лише у тому випадку, коли файл у порядку, і вийти з помилкою, якщо це не так - він просто залишиться там, як ніби все в порядку:

$ 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?

Тож навіть якщо ви самі перенаправляєте висновок у тимчасовий файл і працюєте лише у тому zathuraвипадку, якщо все було нормально, немає гарантії, що користувачеві не буде надано чорне вікно, якщо zathuraвихід із тієї чи іншої причини не подобається. .


Btw,

man -X man

у вікні X11 відобразиться сторінка gxditview, навіть якщо вона виглядає прямо з 70-х ;-)

І, звичайно, завжди можна використовувати:

... | xargs xterm -e man

що, крім багатьох інших вдосконалень, дозволить вам використовувати регулярні вирази при пошуку та правильному підборі тексту.


6

Усі команди в конвеєрі починаються майже одночасно. Синхронізує їх лише введення / виведення над трубою. Також труба може містити лише стільки інформації, скільки дозволяє буфер.

Тому ви не можете уникнути запуску однієї стадії трубопроводу, оскільки

  1. команда на цьому етапі запускається, як тільки все інші етапи запускаються, і
  2. якщо команда не споживає вхід, який надходить над трубою, він блокує попередні етапи конвеєра.

Замість цього запишіть висновок у файл, дозволяючи конвеєру закінчити. Потім використовуйте цей файл.

Приклад (як функція, що бере один аргумент):

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"
}

Це додатково не запустить zathuraпрограму, якщо конвеєр не вдався ( xargsчастина повернулася не нульовою) або згенерований файл порожній.

У bashоболонці ви також можете встановити параметр pipefailоболонки, set -o pipefailщоб трубопровід повертав статус виходу першої команди в трубопроводі, який не працює. І ви хочете зробити tmpfileзмінну 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"
}

Це встановлює pipefailопцію для тривалості функції, якщо вона вже не була встановлена, а потім знімає її за потреби. Це позбавляється від -sтесту на вихідному файлі.


1
Чому rm -f? Чи думаєте ви про випадки, коли труба змінює дозволи tmpfile?
тердон

2
@terdon Я думаю про випадки, коли тимчасовий файл передчасно видалено. rm -fне помилиться, якщо файл уже видалено (можливо zathura, не знаю).
Кусалаланда

Перша функція працює не так, як очікувалося: вона також зробить zathura показати чорне вікно, але тепер zathura працює після закінчення трубопроводу, а не працює поруч із трубопроводом. Це відбувається тому, що конвеєр повертає статус виходу xargs, який дорівнює 0. Команда, яка не працює в конвеєрі, - dmenu (яка повертає 1, коли я нічого не вибираю). Функція bash з pipefailопцією працює як очікувалося (і в zsh теж, який має той самий варіант).
Seninha

1
@Seninha Я виправив першу функцію, дозволивши перевірити, чи створений файл не порожній.
Kusalananda
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.