我在观看后尝试了以下命令这个视频关于管道恶作剧。
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 才能同步它们。此外,管道只能保存管道缓冲区允许的尽可能多的信息。
因此,您无法避免运行管道的一个阶段,因为
- 无论如何,一旦所有其他阶段启动,该阶段中的命令就会启动,并且
- 如果该命令不消耗通过管道传入的输入,它将阻塞管道的前几个阶段。
相反,将输出写入文件,同时让管道完成。然后使用该文件。
示例(作为带有一个参数的函数):
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
shell 中,您可能还需要设置pipefail
shell 选项,以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
对输出文件的测试。