我只能在 bash 中出现捕获错误的情况下显示标准输出/标准输出



我希望它如何工作:

  • 当 bash 捕获没有错误时(如果没有任何内容返回非零退出代码 [除非被 || true 覆盖](,请保持沉默。隐藏标准输出和标准输出。
  • 当错误被 bash 捕获时,要冗长。写标准输出和标准。

在我的脚本中,只有标准输出和标准缺失。

#!/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 捕获错误。如果不需要该临时文件,会更好一些。知道吗?

信用:这个问题的灵感来自问题 如何在 bash 中撤消 exec>/dev/null?查尔斯·达菲的回答

让我们仔细看看 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,但做好工作真的很难。

文件描述符 5 和 6 是只写的。 外壳无法读取自己的输出;双向管道是等待发生的死锁,即使两端的进程不同。

我会选择临时文件的想法。

I/O 文件的实际路径对 shell 和其他应用程序是隐藏的;你需要一个知道如何挖掘细节的程序。如果您的系统支持,LSOF 可能会为您提供帮助。尝试在错误例程中添加以下内容:

本地名称0="$(基本名称"$0"(";lsof -p$$ -d5,6 2>/dev/null |    egrep "^${name0:0:5}[^ ]* +[^ ]+ +[^ ]+ +[56][a-zA-Z]* ">

这将需要一些调整才能使其健壮(短程序名称,其中有空格的程序名称,...(,并且更友好("4"的stdout和"3"的stderr...比如说替换第 1 列中的程序名称(。但是,当您进行调整时,请注意可能会遇到的输出格式的巨大差异。不仅在系统之间,而且在同一系统上的不同文件类型之间。我把这留给学生练习。

最新更新