我需要上传当前脚本的输出,所以我添加了一个trap
and set -ex
,例如
#!/bin/bash
exec &> /tmp/error.log
trap 'cat /tmp/error.log; curl http://127.0.0.1/error.php?hostname=$(hostname) -F file=@/tmp/error.log' EXIT
set -ex
wtfwtf
当我执行它时,我总是收到这个错误,并且 PHP 脚本没有收到整个文件
%> cat /tmp/error.log
1.sh: line 6: wtfwtf: command not found
cat: /tmp/error.log: input file is output file
到目前为止,唯一的解决方案是将error.log复制到一个新文件并上传,例如
#!/bin/bash
exec &> /tmp/error.log
trap 'cp /tmp/error.log 123; curl http://127.0.0.1/error.php?hostname=$(hostname) -F file=@123' EXIT
set -ex
wtfwtf
有没有更好的方法来做到这一点?
使用
exec
,您将脚本的所有输出重定向到特定的日志文件。在您的陷阱中,您希望使用
cat
. 由于所有输出也被重定向到该文件,GNUcat
注意到它的输入文件和标准输出流(从 shell 继承)是同一个东西,并拒绝执行它的任务。BSD
cat
不做与 GNU 相同的检查cat
,如果脚本没有被中断,则会导致一个无限大的日志文件,其中同样的几行一遍又一遍地重复。一种解决方法是保存原始标准输出文件描述符,像以前一样进行重定向,然后在陷阱中恢复它。
这会在将文件描述符 1(作为 fd 3)重定向到日志文件之前对其进行复制。在陷阱中,我们将此副本移回 fd 1 并进行输出。
请注意,在此示例中,陷阱中的标准错误流仍连接到日志文件。因此,如果
curl
生成诊断消息,这将保存在日志文件中,而不是显示在终端(或原始标准错误流连接到的任何位置)上。考虑到 Stéphane Chazelas 的评论:
他的观点是日志文件无论如何只用于诊断消息,因此将日志文件输出到原始标准错误流更有意义。
他还指出,在全局可写目录中使用固定文件名是很危险的,例如
/tmp
. 这是因为脚本中没有进行任何检查以确保该文件不存在(例如,某人或某些恶意软件可能创建了指向您或您的/tmp/error.log
符号链接)。他对此的解决方案是使用专门的持久化日志文件代替下的脚本(该文件是持久化的,但运行脚本时内容会被清除)。/etc/passwd
~/.bashrc
/var/log
对此的一种变体是用于
mktemp
在下面创建一个唯一的文件名$TMPDIR
(然后在EXIT
陷阱中删除该文件,除非curl
失败,在这种情况下rm
不会执行,因为set -e
它已经生效):您的第二个示例有效,但这只是因为您没有
cat
在日志文件上使用,而不是因为复制它。次要的挑剔:命令行上的 URL 可能应该总是至少用双引号引起来,因为它们往往包含 shell 可能解释为特殊的字符(例如
?
)。