Bash 中退出状态不为 0 时将 STDOUT 重定向到 STDERR
Redirect STDOUT to STDERR when exit status isn't 0 in Bash
编程语言 Crystal 当前不会在编译时失败时写入 STDERR。如果退出状态不是 0 并且仍然 return 退出状态,我需要将 STDOUT 重定向到 STDERR。
从字面上看,您的请求实际上是不可能的:重定向是在命令开始之前执行的,而退出状态只有在退出时才知道。
但是,您可以无条件地重定向到缓冲区,然后在知道退出状态后将该缓冲区写入标准输出或标准错误。
考虑类似于以下内容的包装器:
#!/bin/sh
if output=$("$@"); then
printf '%s\n' "$output"
else
retval=$?
printf '%s\n' "$output" >&2
exit "$retval"
fi
...调用为(如果保存为 stderr-wrapper
):
stderr-wrapper your-program arg1 arg2 ...
迂回管道方法,需要tac
和一个无害的eval
,但既没有缓冲区变量,也没有临时文件。
演示脚本demo
,使用echo false ; false
模拟程序输出到stdout和returns一个错误代码“1".
#!/bin/bash
out=([0]=/dev/stdout [1]=/dev/stderr)
{ { echo ; ; echo $? ; } | tac ; } | \
{ read x ; eval tac \> ${out[$x]} ; exit $x ; }
证明,使用 annotate-output
:
annotate-output +'' ./demo true ; echo --- ; annotate-output +'' ./demo false
I: Started ./demo true
O: true
I: Finished with exitcode 0
---
I: Started ./demo false
E: false
I: Finished with exitcode 1
将 demo
概括为 stderr-wrapper
:
对于bash
:
#!/bin/bash
# Usage: stderr-wrapper program [ args... ]
out=([0]=/dev/stdout [1]=/dev/stderr)
{ { $@ ; echo $? ; } | tac ; } | \
{ read x ; eval tac \> ${out[$((x>0))]} ; exit $x ; }
对于POSIX shell,(这里是dash
):
#!/bin/dash
# Usage: stderr-wrapper program [ args... ]
{ { "$@" ; echo $? ; } | tac ; } | \
{ read x ; eval tac 1\>\&$(((x>0)+1)) ; exit $x ; }
bash
版本的工作原理:
- 运行 有问题的程序,输出到 stdout.
- 之后立即打印错误代码,也输出到 stdout。
- 用
tac
反转整个流,所以错误码在前。 (如果流很长,则价格昂贵。)
read
一行,错误码,存放在$x
.
eval
$out
数组成员对应于输出应该去的设备,然后用 tac
重新反转输出到那个设备。
编程语言 Crystal 当前不会在编译时失败时写入 STDERR。如果退出状态不是 0 并且仍然 return 退出状态,我需要将 STDOUT 重定向到 STDERR。
从字面上看,您的请求实际上是不可能的:重定向是在命令开始之前执行的,而退出状态只有在退出时才知道。
但是,您可以无条件地重定向到缓冲区,然后在知道退出状态后将该缓冲区写入标准输出或标准错误。
考虑类似于以下内容的包装器:
#!/bin/sh
if output=$("$@"); then
printf '%s\n' "$output"
else
retval=$?
printf '%s\n' "$output" >&2
exit "$retval"
fi
...调用为(如果保存为 stderr-wrapper
):
stderr-wrapper your-program arg1 arg2 ...
迂回管道方法,需要tac
和一个无害的eval
,但既没有缓冲区变量,也没有临时文件。
演示脚本demo
,使用echo false ; false
模拟程序输出到stdout和returns一个错误代码“1".
#!/bin/bash
out=([0]=/dev/stdout [1]=/dev/stderr)
{ { echo ; ; echo $? ; } | tac ; } | \
{ read x ; eval tac \> ${out[$x]} ; exit $x ; }
证明,使用 annotate-output
:
annotate-output +'' ./demo true ; echo --- ; annotate-output +'' ./demo false
I: Started ./demo true
O: true
I: Finished with exitcode 0
---
I: Started ./demo false
E: false
I: Finished with exitcode 1
将 demo
概括为 stderr-wrapper
:
对于
bash
:#!/bin/bash # Usage: stderr-wrapper program [ args... ] out=([0]=/dev/stdout [1]=/dev/stderr) { { $@ ; echo $? ; } | tac ; } | \ { read x ; eval tac \> ${out[$((x>0))]} ; exit $x ; }
对于POSIX shell,(这里是
dash
):#!/bin/dash # Usage: stderr-wrapper program [ args... ] { { "$@" ; echo $? ; } | tac ; } | \ { read x ; eval tac 1\>\&$(((x>0)+1)) ; exit $x ; }
bash
版本的工作原理:
- 运行 有问题的程序,输出到 stdout.
- 之后立即打印错误代码,也输出到 stdout。
- 用
tac
反转整个流,所以错误码在前。 (如果流很长,则价格昂贵。) read
一行,错误码,存放在$x
.eval
$out
数组成员对应于输出应该去的设备,然后用tac
重新反转输出到那个设备。