我是否只能在bash中出现陷阱错误时显示stdout / stderr?

时间:2013-07-26 21:18:04

标签: bash

我希望它如何运作:

  • 当没有错误被bash捕获时(如果没有返回非零退出代码[除非被|| true覆盖]),请保持沉默。隐藏stdout和stderr。
  • 当错误被bash困住时,请详细说明。写stdout和stderr。

在我的脚本中只缺少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

3 个答案:

答案 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>