POSIX shell (sh) 将 stderr 重定向到 stdout 并将 stderr 和 stdout 捕获到变量中

Jim*_*mix 5 posix stdout sh stderr

我想将 stderr 重定向到 stdout,以便终端在命令执行期间打印它们,但我也想将它们捕获到单独的变量中。我设法在 Bash 中实现了这一点(版本 4.4.20(1)-release):

\n
#!/bin/bash\n\necho "terminal:"\n{ err="$(find / -maxdepth 2 -iname 'tmp' -type d 2>&1 1>&3 3>&- | tee /dev/stderr)"; ec="$?"; } 3>&1 | tee /dev/fd/4 2>&1; out=$(cat /dev/fd/4)\necho "stdout:" && echo "$out"\necho "stderr:" && echo "$err"\n
Run Code Online (Sandbox Code Playgroud)\n

这给出了所需的:

\n
terminal:\nfind: \xe2\x80\x98/root\xe2\x80\x99: Permission denied\n/tmp\n/var/tmp\nfind: \xe2\x80\x98/lost+found\xe2\x80\x99: Permission denied\nstdout:\n/tmp\n/var/tmp\nstderr:\nfind: \xe2\x80\x98/root\xe2\x80\x99: Permission denied\nfind: \xe2\x80\x98/lost+found\xe2\x80\x99: Permission denied\n
Run Code Online (Sandbox Code Playgroud)\n

但我在将该脚本转换为 POSIX sh 时遇到问题/bin/sh

\n
#!/bin/sh\n\necho "terminal:"\n{ err="$(find / -maxdepth 2 -iname 'tmp' -type d 2>&1 1>&3 3>&- | tee /dev/stderr)"; ec="$?"; } 3>&1 | tee /dev/fd/4 2>&1; out=$(cat /dev/fd/4)\necho "stdout:" && echo "$out"\necho "stderr:" && echo "$err"\n
Run Code Online (Sandbox Code Playgroud)\n

给出:

\n
terminal:\ntee: /dev/fd/4: No such file or directory\nfind: \xe2\x80\x98/root\xe2\x80\x99: Permission denied\n/tmp\n/var/tmp\nfind: \xe2\x80\x98/lost+found\xe2\x80\x99: Permission denied\ncat: /dev/fd/4: No such file or directory\nstdout:\n\nstderr:\n
Run Code Online (Sandbox Code Playgroud)\n

/dev/fd/4不存在,也不存在/proc/self/fd/4

\n

如何使该脚本作为 POSIX shell 脚本工作?

\n

Dan*_*tos 0

好吧,我在 busybox 的ashshell 中执行此操作,我的选择相当有限,但这就是我想出的可行方法。

tmpfile=/tmp/some_prefix_stderr.$$
out=$(my_cmd) 2>$tmpfile
result=$?
err=$(cat $tmpfile)
rm $tmpfile
Run Code Online (Sandbox Code Playgroud)

它不是最漂亮的,但它很有效。一般来说,some_prefix用特定的东西替换可以帮助避免冲突,但只要有 PID 就可以解决这个问题,除非你正在运行多个线程。

如果您可以依靠程序在 stderr 中显示某些内容时以非零退出,则可以稍微简化一下:

tmpfile=/tmp/some_prefix_stderr.$$
if ! out=$(my_cmd) 2>$tmpfile; then
    # specific behavior here
    cat $tmpfile >&2
fi
rm $tmpfile
Run Code Online (Sandbox Code Playgroud)

这完全符合 POSIX 标准。请务必清理您的临时文件。