如何使管道等待文件结尾或在发生错误后停止?

如何使管道等待文件结尾或在发生错误后停止?

我在观看后尝试了以下命令这个视频关于管道恶作剧。

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

它基本上将手册页列表打印到 dmenu 供用户选择其中一个,然后使用 xargs 运行man -Tpdf %(从 xargs 的输入打印到 stdout 手册页 git 的 pdf)并将 pdf 传递给 pdf 阅读器(zathura )。

问题是(正如您在视频中看到的)pdf 阅读器甚至在我在 dmenu 中选择一个联机帮助页之前就启动了。如果我单击 Esc 并选择“无”,pdf 阅读器仍处于打开状态,根本不显示任何文档。

如何使 pdf 阅读器(以及管道链中的任何其他命令)仅在其输入到达文件末尾或收到输入时运行?或者,如何使管道链在链接命令之一返回非零退出状态后停止(这样,如果 dmenu 由于未选择选项而返回错误,则不会运行以下命令)?

答案1

如何使 pdf 阅读器(以及管道链中的任何其他命令)仅在其输入到达文件末尾或收到输入时运行?

ifne(在 Debian 中它位于moreutils包中):

ifne当且仅当标准输入不为空时运行以下命令。

在你的情况下:

… | ifne zathura -

答案2

Pdf 文件应该是可查找的;任何 pdf 查看器都必须首先查看预告片,然后从那里跳转到外部参照表的偏移量。

由于管道不可查找,zathura因此使用了一种混淆技巧,即将所有输入复制到临时文件,然后像平常一样使用该临时文件。这种“聪明”的伎俩正在制造虚假的希望,并导致人们认为 pdf 文件是可流式传输的。

但无论如何,zathura真的在显示文档之前等待 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?

因此,即使您自己将输出重定向到临时文件,并且仅zathura在一切正常的情况下运行,也不能保证如果用户zathura因某种原因不喜欢输出,则不会看到黑色窗口。


顺便提一句,

man -X man

将在 X11 窗口中显示联机帮助页gxditview,即使它看起来直接来自 '70 ;-)

当然,您始终可以使用:

... | xargs xterm -e man

除了许多其他增强功能之外,它还允许您在搜索和正确的文本选择中使用正则表达式。

答案3

管道中的所有命令几乎同时启动。只有管​​道上的 I/O 才能同步它们。此外,管道只能保存管道缓冲区允许的尽可能多的信息。

因此,您无法避免运行管道的一个阶段,因为

  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部分返回非零)或生成的文件为空,这也不会运行程序。

bashshell 中,您可能还需要设置pipefailshell 选项,以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对输出文件的测试。

相关内容