我有一个脚本,它调用一个程序(具体来说,ttf2afm
是 tetex 3.0 的一部分),有时会出现段错误,有时不会。我需要的信息总是被打印出来前它存在段错误,但我很难阻止管道重定向失败,并且在程序失败时不向管道输出任何内容。
true
我尝试过通过 FIFO 重定向,在末尾用 a 括起进程,从 shell 函数执行并封装在 中sh -c
,但脚本似乎永远不会让进程输出任何事物、重定向或其他方式——甚至不是 stderr。
我知道它能够输出,因为它完全能够从命令行给出它,但由于某种原因不能从脚本给出。
我的问题是,脚本有什么办法可以忽略程序段错误的事实并给我输出吗?
我正在运行 BASH 4.1.10(2)-release。
答案1
程序通常会缓冲其输出以提高效率。也就是说,它们将输出累积在内存区域(称为缓冲区)中,只有当缓冲区已满或在程序中的某些关键点时,它们才会真正输出输出。当程序正常结束时,它会刷新输出缓冲区(即打印出其中剩余的任何数据)。当出现段错误时,缓冲区的内容就会丢失。
直接在终端中运行程序时,您不会观察到这种效果,因为当程序的输出连接到终端(而不是常规文件或管道)时,行为会有所不同。在终端中,默认行为是在每行末尾刷新缓冲区。因此,您将看到程序出现段错误之前生成的每条完整行。
您可以强制程序在终端中运行并收集其输出。最简单的方法是运行script
。您需要解决许多烦恼:
script
向转录文件添加标题行,之后您需要将其删除。script
不返回命令的状态代码,因此如果您想了解段错误或任何其他错误,则需要将其保存在某个位置。script
会导致正常输出和错误输出;您最好将错误输出保存到单独的文件中。
export FONT="foo"
script -q -c '
ttf2afm "$FONT.ttf" 2>"$FONT.ttf2afm-err";
echo $? >"$FONT.ttf2afm-status"
' "$FONT.ttf2afm-typescript"
tail -n +2 <"$FONT.ttf2afm-typescript" >"foo.afm"
rm "$FONT.ttf2afm-typescript"
if [ "$(cat "$FONT.ttf2afm-status")" -ne 0 ]; then
echo 1>&2 "Warning: ttf2afm failed"
cat "$FONT.ttf2afm-err"
fi
答案2
经过不断的摸索和摸索,我终于想通了。解决方案有点复杂:
(trap 'true' ERR; exec ttf2afm "$FONT") |
grep ...
显然,exec
导致ttf2afm
子 shell 进程被捕获的错误接管,导致它在一个无论是否出现段错误都无关紧要的环境中运行。
当程序失败时,捕获全包ERR
信号将阻止子 shell 死亡并向主脚本发送信号(如果发生,主脚本将立即终止)。
唯一的问题是内核本身将输出一大堆堆栈跟踪垃圾直接连接到控制台设备一旦进程出现段错误,所以没有办法阻止它被输出[据我所知],但这并不重要,因为它不会影响标准输出或标准错误。