将多个输入通过管道传输到 Ghostscript

将多个输入通过管道传输到 Ghostscript

我正在尝试使用 Ghostscript 创建一个单行 Linux 命令来合并两个 PDF 文件(从 URL 下载)。然而,我不想创建任何临时文件(一切都应该在记忆中完成)。

以下命令似乎不起作用(我尝试通过进程替换来实现此目的)。

gs -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=combined.pdf <(curl http://example.com/one.pdf) <(curl http://example.com/two.pdf)

当我运行此命令时,它给出以下错误。

**** Warning:  An error occurred while reading an XREF table.
**** The file has been damaged.  This may have been caused
**** by a problem while converting or transfering the file.
**** Ghostscript will attempt to recover the data.
Error: /ioerror in --run--
Current allocation mode is local
Last OS error: Illegal seek
GPL Ghostscript 9.18: Unrecoverable error, exit code 1

我相信正在发生的事情是在两个输入 PDF 有机会完成下载之前运行 Ghostscript 命令,也许有一种方法可以等待这种情况发生。

答案1

这不是您正在寻找的答案,但它是一个实用的替代方案。


首先,一些背景:

所有管道类型都有读者无法在其中找到的缺点,包括 Bash 使用的管道类型<(command-list)。我不知道 GhostScript 是否需要在输入文件中查找,或者它是否只是读取内存中的整个文件,但一般来说,对于许多文件格式,基于管道的输入可能会更慢或使用更多内存(由于无查找)解决方法)而不是文件输入。

(我不确定,因为我倾向于稳健性。我希望我的脚本首先能够工作(给我可靠的结果),其次是轻量级和快速的。由于下面列出的原因,避免临时文件不会给我带来加速或更少的资源使用在我使用的系统上。)

在许多(大多数?)Linux 发行版中,Solaris(自 SunOS 4 和 Solaris 2.1 起)、NetBSD(4.0 及更高版本)、FreeBSD(7.0 及更高版本)、DragonFly BSD 和 OpenBSD(5.5 及更高版本)/tmp通常是临时文件系统,一个基于 RAM 的文件系统。在这些系统上,避免临时文件会适得其反(除非您绝对知道您提供给它的应用程序可以将输入作为流处理,即通过管道,没有缺点)。

典型的反例(即当您使用管道而不是临时文件时的情况)是通过例如sed或进行简单的过滤或处理awk。 GhostScript绝对不是“一个简单的过滤或处理程序”。

当前的所有操作系统都足够智能,可以将最近使用的文件保存在内存中,而不是从存储中写入和读取它们。 (他们通常会节省在这种临时文件情况下,内容一次写入磁盘,但不会读回。)因此,即使/tmp不是基于 RAM 的文件系统,临时文件也会保留在内存中,最多只写入磁盘一次。这意味着,即使在非基于 RAM 的系统中/tmp,临时文件也是一个不错的选择。

总而言之,上述意味着在正常使用情况下,您需要不是希望避免临时文件,尤其是当它们被送入转换器或应用程序进行处理时。

事实上,通常希望避免临时文件的原因是以下一些变体

我不希望我的脚本在我中断它们时留下不必要的临时文件,或者它们失败/因错误退出/无法正常工作。

使用 Bash 和mktemp实用程序,避免这种情况是微不足道的。我已经使用以下习语好几年了:

#!/bin/bash
Work=$(mktemp -d) || exit 1
trap "cd / ; rm -rf '$Work'" EXIT

这将创建一个临时目录(在 下/tmp/),当脚本退出时该目录将自动删除。 (Linux coreutilsmktemp使该目录只能由所有者用户访问;组或其他用户无法访问,因此这也非常安全。)

bash 内置命令trap是这样制定的(通过这些特定的引号),这样即使您稍后更改了环境变量,正确的(原始)临时目录也将被删除,因为设置Work时变量会扩展trap而不是当陷阱触发时。

经过上述操作后,您可以使用例如

curl 'http://www.example.com/one.pdf' > "$Work/one.pdf" || exit 1
curl 'http://www.example.com/two.pdf' > "$Work/two.pdf" || exit 1

gs -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=combined.pdf "$Work/one.pdf" "$Work/two.pdf" || exit 1

此后无需添加任何清理,因为退出陷阱将自动处理该问题 - 即使您使用Ctrl+C或其他信号中断脚本。

相关内容