我希望它如何运作:
在我的脚本中只缺少stdout和stderr。
#!/bin/bash
exec 5>&1 >/dev/null
exec 6>&2 2>/dev/null
error_handler() {
local return_code="$?"
local last_err="$BASH_COMMAND"
local stdout= # How to read FD 5?
local stderr= # How to read FD 6?
exec 1>&5
exec 2>&6
echo "ERROR!
scriptname: $0
BASH_COMMAND: $last_err
\$?: $return_code
stdout: $stdout
stderr: $stderr
" 1>&2
exit 1
}
trap "error_handler" ERR
echo "Some message..."
# Some command fails, i.e. return a non-zero exit code.
mkdir
我可能会将stdout / stderr重定向到一个临时文件,并使用cat来显示它,以防错误被bash困住。如果不需要该临时文件,会好一点。有什么想法吗?
信用: 这个问题的灵感来自问题How to undo exec > /dev/null in bash?和answer Charles Duffy
答案 0 :(得分:3)
让我们仔细看看I / O重定向:
exec 5>&1 >/dev/null
exec 6>&2 2>/dev/null
我们看到文件描述符5是原始标准输出的副本,但该标准输出将转到/dev/null
。同样,6是标准错误的副本,但标准错误是/dev/null
。
现在让我们考虑一下你跑步时会发生什么:
ls -l /dev/null /dev/not-actually/there
ls
命令将/dev/null
的输出写入/dev/null
,因为这是其标准输出所针对的位置。同样,它会将不存在的文件/dev/not-actually/there
的错误写入/dev/null
,因为这是其标准错误所针对的位置。
因此,命令的标准输出和标准错误都不可避免地丢失。
鉴于表达的要求,不会有一个简单的解决方案。您最好的选择可能是将标准输出和标准错误重定向到同一个文件(但请注意,错误和正常输出的交错可能会有所不同,因为输出是文件)。或者,您可以将标准输出和标准错误指向两个单独的文件,并在必要时显示它们。
请注意,您需要考虑在每个命令之后清空输出文件(让trap
在清空文件之前报告内容),这样您就不会报告命令10失败时命令1-9的标准输出或标准错误。
这样做整齐,正确处理管道等等,并非易事。我不确定是否建议传递命令和参数的函数(对于管道来说很棘手)或其他一些技术。
我在cron
中使用了“在一个文件中捕获所有内容”技术 - 运行适当时邮件输出的脚本。它并不完全令人满意,但它比没有错误消息要好得多。
您可以考虑使用expect
和/或伪ttys,但做得很好将非常困难。
答案 1 :(得分:2)
您的文件描述符5和6是只写的。 shell无法读取自己的输出;双向管道是等待发生的死锁,即使两端的进程不同。
我会选择临时文件。
答案 2 :(得分:0)
I / O文件的实际路径在shell和其他应用程序中是隐藏的;你需要一个知道如何挖掘细节的程序。如果您的系统支持, lsof 可能会帮助您解决问题。尝试在错误例程中添加以下内容:
local name0="$(basename "$0")"; lsof -p$$ -d5,6 2>/dev/null | egrep "^${name0:0:5}[^ ]* +[^ ]+ +[^ ]+ +[56][a-zA-Z]* "
这将需要一些调整才能使它变得健壮(短程序名,带有空格的程序名,......),以及更友好(stdout为“4”和“stderr for”3“......比如在第1列中替换程序名称。但是当你调整时要注意你可能遇到的输出格式的巨大变化。不仅在系统之间,而且在同一系统上的不同文件类型之间。我将此作为练习留给学生。 / p>