web-dev-qa-db-ja.com

パイプラインにファイルの終わりを待機させる方法、またはエラー後に停止する方法

ウォッチの後に次のコマンドを試してみました このビデオ パイプシェニガンで。

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

基本的に、ユーザーがそれらの1つを選択できるようにdmenuにマンページのリストを出力し、xargsを使用してman -Tpdf %(xargsの入力からマンページgitのpdfを標準出力に出力する)を実行し、pdfをPDFリーダー(zathura)。

問題は(ビデオでわかるように)dmenuでマンページを1つ選択する前でもpdfリーダーが起動することです。 Escをクリックして何も選択しない場合でも、PDFリーダーは開いたままで、ドキュメントはまったく表示されません。

PDFリーダー(およびパイプチェーン内の他のコマンド)が、その入力がファイルの終わりに到達したとき、またはまったく入力を受信したときにのみ実行されるようにするにはどうすればよいですか?または、代わりに、チェーンされたコマンドの1つがゼロ以外の終了ステータスを返した後、パイプチェーンを停止させるにはどうすればよいですか(dmenuがオプションを選択しないというエラーを返した場合、次のコマンドは実行されません)?

12
Seninha

PDFリーダー(およびパイプチェーン内の他のコマンド)が、入力がファイルの終わりに到達したとき、または入力を受信したときにのみ実行されるようにするにはどうすればよいですか?

ifne があります(Debianではmoreutilsパッケージにあります):

ifneは、標準入力が空でない場合にのみ、次のコマンドを実行します。

あなたの場合:

… | ifne zathura -
12

パイプライン内のすべてのコマンドは、ほぼ同時に開始されます。それらを同期するのは、パイプを介したI/Oのみです。また、パイプが保持できる情報は、パイプのバッファが許す限り多くなります。

したがって、パイプラインの1つのステージの実行を回避できません。

  1. とにかく、他のすべてのステージが開始されるとすぐに、そのステージのコマンドが開始されます。
  2. コマンドがパイプ経由で着信する入力を消費しなかった場合、パイプラインの前のステージがブロックされます。

代わりに、パイプラインを終了させながら、出力をファイルに書き込みます。次に、そのファイルを使用します。

例(1つの引数を取る関数として):

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テストを取り除きます。

6
Kusalananda

PDFファイルはシーク可能であることになっています。すべてのPDFビューアは、最初にトレーラーを見て、そこから外部参照テーブルからのオフセットにジャンプする必要があります。

パイプはシークできないため、zathuraは、すべての入力を一時ファイルにコピーし、その一時ファイルを通常どおりに使用する難読化のトリックを使用しています。この種の「巧妙な」トリックは、誤った希望を生み出し、PDFファイルはストリーミング可能であると人々に思わせています。

しかし、とにかく、zathuraは本当にdoes EOFを待ってからドキュメントを表示します。これを行うために何もする必要はありません。

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

したがって、出力を自分で一時ファイルにリダイレクトしていて、すべてがOKの場合にのみzathuraを実行している場合でも、zathuraが何らかの理由で出力を気に入らなかった場合にユーザーに黒いウィンドウが表示されないという保証はありません。または別の。


ところで、

man -X man

たとえそれが'70からまっすぐに見えたとしても、gxditviewでX11ウィンドウにマンページを表示します;-)

そしてもちろん、いつでも使用できます:

... | xargs xterm -e man

他の多くの機能強化に加えて、検索や適切なテキスト選択で正規表現を使用できます。

6
mosvy