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


12

パイプシェナンガンに関するこのビデオを見て、次のコマンドを試しました。

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をクリックしてnoneを選択すると、pdfリーダーは開いたままで、ドキュメントがまったく表示されません。

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


1
どのシェルを使用していますか?これはバッシュですか?
terdon

私はbash、zsh、shで試しました。それらはすべて同じ動作をしました。
Seninha

2
はい、動作は標準pipefailです。Kusalandandaの回答で言及されているbashのオプションのため、どのシェルを尋ねました。
terdon

回答:


12

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

ありifne(Debianではそれが中だmoreutilsパッケージ):

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

あなたの場合:

 | ifne zathura -

答えをありがとう、私はこのコマンドを知りませんでした!このコマンド(およびの他のコマンドmoreutils)は、元のUnixにあり、posixで指定されている必要があります...これは、このような基本的でUnix風のツールです...
Seninha

@Seninhaの単純さはifne少し誤解を招きます。Unixには「パイプピーク」操作がないためifne、依存コマンドの実行を決定する前に、実際には少なくとも1バイトを読み取る必要があります。つまり、テストを実行してコマンドを実行するだけではなく、別のパイプを作成し、別のプロセスをフォークして依存コマンドを実行し、ストリーム全体をstdinパイプからダウンストリームパイプにコピーする必要があります。「入力が空」のケースが一般的でない場合ifne、平均して節約するよりも簡単に多くのリソースを費やす可能性があります。

@ Wumpus.Q.Wumbleyは神話です。パイプにデータがあるかどうかを判断するためにバイトを読み取る必要はありません。こちらをご覧ください。Linuxでは、実際にパイプからデータをピークすることができます(つまり、データを削除せずに読み取ります)。ここで「標準的な」回答へのコメントでこれについて言及しましたが、おそらくそれらの事実は回答のすごさを損なうようなものだと感じたため、MODによって削除されました。
mosvy

6

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

パイプはシークできないため、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何らかの理由で出力が気に入らなければ、ユーザーに黒いウィンドウが表示されないという保証はありません。 。


ところで、

man -X man

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

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

... | xargs xterm -e man

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


6

パイプラインのすべてのコマンドはほぼ同時に開始されます。それらを同期するのはパイプ上の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出力ファイルのテストを取り除きます。


1
なんでrm -f?パイプがtmpfileの許可を変更する場合を考えていますか?
テルドン

2
@terdon一時ファイルが時期尚早に削除される場合を考えています。rm -fファイルがすでに削除されていてもエラーにならないでしょう(おそらくzathura、私にはわかりません)。
クサラナナンダ

最初の関数は期待どおりに機能しません:また、zathuraに黒いウィンドウが表示されますが、zathuraはパイプラインと並行して実行されるのではなく、パイプラインの終了後に実行されます。これは、パイプラインが0であるxargsの終了ステータスを返すためです。パイプラインで失敗するコマンドはdmenuです(何も選択しないと1を返します)。pipefailオプション付きのbash関数は期待どおりに機能します(zshでも同じオプションがあります)。
セニニャ

1
@Seninha最初の関数を修正して、生成されたファイルが空でないかどうかをチェックさせました。
Kusalananda
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.