我有一个 Bash 函数,用于在 PDF 中显示以 postscript 形式呈现的手册页:
function psman () {
man -t "$@" | ps2pdf - /tmp/manpage.pdf
evince /tmp/manpage.pdf
}
(更新:我去掉了诸如动态生成临时文件名和使用“nohup”之类的外围复杂因素)
这很好用。要查看使用截图,请参阅https://www.tartley.com/postscript-formatted-man-pages。
为了我自己的启发,我尝试在不使用临时文件的情况下实现它。例如,使用进程替换:
$ evince <(man -t ls | ps2pdf - -)
这不起作用。Evince 在其 GUI 中显示错误:
Unable to open document "file:///dev/fd/63".
PDF document is damaged
为什么?如何才能生成并查看 PDF 而不生成任何中间文件?
上述错误消息与 evince 显示的丢失或空文件的消息不同,所以不仅仅是那样。
更新:为了获取更多信息,我尝试用“ls”替换“evince”:
$ ls -l <(man -t ls | ps2pdf - -)
lr-x------. 1 jhartley jhartley 64 Aug 23 08:59 /dev/fd/63 -> pipe:[196475]
其中 dircolors 是颜色:
/dev/fd/63
作为“ORPHAN”(指向不存在文件的符号链接),以及pipe:[196475]
为“MISSING”(符号链接指向不存在的文件)
那么也许 evince 只是被赋予了一个指向不存在的文件的链接?为了模拟这种情况,我创建了一个指向不存在文件的符号链接,然后用“evince”打开它。但是它并没有显示上面的“PDF 已损坏”消息,而是显示“没有这样的文件或目录”。
更新:我认为 ORPHAN/MISSING 文件类型是用来转移注意力的。我在进行非常简单的进程替换时看到了相同的 ORPHAN/MISSING 符号链接:
$ ls -l <( echo 123 )
man|ps2pdt
当将进程替换输入到时,使用相同的管道可以正常工作diff
:
$ diff <(man -t ls | ps2pdf - - | tr "\0" "0") <(man -t ls | ps2pdf - - | tr "\0" "0")
248c248
< /ID [<95A81B38FAE8E6FE3C899586A1DEE861><95A81B38FAE8E6FE3C899586A1DEE861>]
---
> /ID [<2F9164BD9265C8540A4A8E7068076344><2F9164BD9265C8540A4A8E7068076344>]
(在这里我在管道中添加了“tr”以消除 pdf 输出中的空/零字符,这样 diff 就会将文件视为文本而不是二进制。)
所以,总而言之,我不知道为什么会出现上述“PDF 已损坏”错误。除了理解之外,我的目标是查看生成的 PDF,而不在此过程中生成任何文件。
答案1
只是一个猜测,但似乎合理:
evince
通过“文件”进行搜索,它得到的流是不可搜索的。比较为什么 BASH 进程替换对某些命令不起作用?
这意味着,如果没有任何中间文件,几乎不可能实现你想要的结果。我能想到的最好的方法是这样的脚本:
#!/bin/bash
tmpd="/dev/shm"
( tmpf="$(mktemp -p "$tmpd" "tmp [man $*] XXX.pdf")"
man -t "$@" | ps2pdf - > "$tmpf"
evince "$tmpf"
rm "$tmpf" ) 2>/dev/null &
备注、缺陷等:
- 当
$tmpd
/dev/shm
在记忆中。我猜它尽可能接近“不生成任何中间文件”,同时保持可查找性。 - 无论它在哪里,我们都应该在之后将其删除。如果脚本在和之间被中断(例如使用Ctrl+ C),则文件仍然存在,而我们不需要它。解决这个问题的方法很少,如果需要,您可以发出信号;我选择在后台运行整个序列(),这可能足够了。
mktemp
rm
trap
( … ) &
- 我的
evince
不会打开来自 的文件,/dev/shm
除非其名称以 结尾.pdf
(此行为不区分大小写)。这就是为什么.pdf
文件名模板中存在 。 中没有这样的问题/tmp
。为什么?我不知道。 - 其中创建了文件名模板
$*
以使其具有一定意义(它显示在evince
窗口的标题中)。
答案2
PDF 文件是一组相互关联的对象,用 ID 标识。在文件末尾,有一个对象索引,它将 ID 映射到文件偏移量。如果没有此索引,则无法使用 PDF 文件,因此读取 PDF 文件的常用方法是寻找接近末尾的位置并尝试找到索引的开头,然后将其读入内存。索引指示哪个对象是根对象,从那里您可以遍历对象图,始终使用索引来查找每个相关对象的文件偏移量。
理论上,你可以将整个文件读入(或 mmap)内存,但对于非常大的文件,这种方法行不通,而 PDF 旨在能够处理非常大的文件(事实上,打印质量 PDF 文件可能非常大)。因此,查找是使用 PDF 文件的固有部分,而进程替换不支持查找。
还有其他命令行应用程序需要查找,或者认为它们需要查找。(有时查找只是程序员为了方便而试图确定文件的大小。)还有其他文件格式将索引放在末尾(例如 Zip 压缩),并且确实依赖于查找。例如,数据库实际上甚至没有线性读取的感觉,可能没有人会想到通过进程替换来提供数据库支持文件。但 PDF 是非线性处理的一种典型代表,这有时令人惊讶。
答案3
您只需添加文件名,例如使用:
(man -t ls | ps2pdf - ~/man_ls.pdf) > evince
这将man_ls.pdf
在你的主目录中创建文件