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"\nRun Code Online (Sandbox Code Playgroud)\n这给出了所需的:
\nterminal:\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\nRun Code Online (Sandbox Code Playgroud)\n但我在将该脚本转换为 POSIX sh 时遇到问题/bin/sh
#!/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"\nRun Code Online (Sandbox Code Playgroud)\n给出:
\nterminal:\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:\nRun Code Online (Sandbox Code Playgroud)\n/dev/fd/4不存在,也不存在/proc/self/fd/4。
如何使该脚本作为 POSIX shell 脚本工作?
\n好吧,我在 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 标准。请务必清理您的临时文件。