“T恤” 和退出状态
是否有替代 tee 捕获 标准输出 和 正在执行的命令的标准错误,并以与处理的命令相同的退出状态退出?
如下所示:
eet -a some.log -- mycommand --foo --bar
其中“eet”是“tee”的想象替代品:)(-a表示追加,--
分隔捕获的命令)。 破解这样的命令应该不难,但也许它已经存在而我不知道?
Is there an alternative to tee which captures standard output and standard error of the command being executed and exits with the same exit status as the processed command?
Something like the following:
eet -a some.log -- mycommand --foo --bar
Where "eet" is an imaginary alternative to "tee" :) (-a means append and --
separates the captured command). It shouldn't be hard to hack such a command, but maybe it already exists and I'm not aware of it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
发布评论
评论(9)
我在 Capture Exit Code using Pipe & 中偶然发现了一些有趣的解决方案。 T恤。
Bash 中有可用的 $PIPESTATUS 变量:
<前><代码>假 | 三通/dev/null
[ $PIPESTATUS -eq 0 ] || 退出$PIPESTATUSPerl 中“eet”的最简单原型可能如下所示:
打开 MAKE,“命令 2>&1 |” 或者死; open (LOGFILE, ">>some.log") or die; while () { 打印日志文件 $_; 打印 } 关闭MAKE; # 获得$? 我的 $exit = $? >>> 8; 关闭日志文件;
这是一个eet
。 适用于我能接触到的所有 Bash,从 2.05b 到 4.0。
#!/bin/bash
tee_args=()
while [[ $# > 0 && $1 != -- ]]; do
tee_args=("${tee_args[@]}" "$1")
shift
done
shift
# now ${tee_args[*]} has the arguments before --,
# and $* has the arguments after --
# redirect standard out through a pipe to tee
exec | tee "${tee_args[@]}"
# do the *real* exec of the desired program
exec "$@"
(pipefail
和 $PIPESTATUS
很好,但我记得它们是在 3.1 左右引入的。)
这是我认为最好的纯 Bourne-shell 解决方案,可用作构建“eet”的基础:
# You want to pipe command1 through command2:
exec 4>&1
exitstatus=`{ { command1; echo $? 1>&3; } | command2 1>&4; } 3>&1`
# $exitstatus now has command1's exit status.
我认为这是从内到外的最佳解释 - command1
将在 stdout(文件描述符 1)上执行并打印其常规输出,然后一旦完成,echo
将在其 stdout 上执行并打印 command1
的退出代码,但该 stdout被重定向到文件描述符三。
当 command1
运行时,它的 stdout 会通过管道传输到 command2(echo
的输出永远不会到达 command2,因为我们将其发送到文件描述符 3 而不是 1,即管道读取的内容)。 然后我们将command2的输出重定向到文件描述符4,这样它也不会出现在文件描述符1之外——因为我们希望当我们带来echo
时文件描述符1被清除文件描述符三上的输出返回到文件描述符一中,以便命令替换(反引号)可以捕获它。
最后一点神奇之处在于,我们将第一个 exec 4>&1
作为一个单独的命令执行 - 它打开文件描述符 4 作为外部 shell 标准输出的副本。 命令替换将从标准输出内部命令的角度捕获在标准输出上写入的所有内容 - 但是,由于就命令替换而言,command2 的输出将写入文件描述符 4,因此命令替换不会捕获它——但是,一旦它“脱离”命令替换,它实际上仍然会转到脚本的整体文件描述符之一。
(exec 4>&1
必须是一个单独的命令才能与许多常见的 shell 一起使用。在某些 shell 中,如果您只是将它放在与变量赋值相同的行上,在结束之后,它就可以工作替换的反引号。)
(在我的示例中,我使用复合命令 ({ ... }
),但子 shell (( ... )
) 也可以工作。 subshell 只会导致子进程的冗余分叉和等待,因为管道的每一侧和命令替换的内部通常已经意味着子进程的分叉和等待,并且我不知道正在编码任何 shell认识到它可以跳过其中一个分叉,因为它已经完成或即将执行另一个分叉。)
您可以以一种技术含量较低且更有趣的方式来看待它,就好像命令的输出正在相互跨越:< code>command1 通过管道传输到 command2
,然后 echo
的输出会跳过 command2
,以便 command2
> 没有捕获到它,然后 command2
的输出会跳过命令替换,就像 echo
及时着陆并被替换捕获一样,这样它最终出现在变量中,command2
的输出继续到达标准输出,就像在普通管道中一样。
另外,据我了解,在此命令结束时, $?
仍将包含管道中第二个命令的返回码,因为变量赋值、命令替换和复合命令都是有效的对于它们内部命令的返回代码是透明的,因此 command2
的返回状态应该被传播出去。
需要注意的是,command1
有可能在某个时刻最终使用文件描述符 3 或 4,或者 command2
或任何后续命令将使用文件描述符 4 ,所以为了更卫生,我们会这样做:
exec 4>&1
exitstatus=`{ { command1 3>&-; echo $? 1>&3; } 4>&- | command2 1>&4; } 3>&1`
exec 4>&-
命令从启动它们的进程继承文件描述符,因此整个第二行将继承文件描述符 4,复合命令后跟 3>&1
将继承文件描述符三。 因此 4>&-
确保内部复合命令不会继承文件描述符 4,而 3>&-
确保 command1< /code> 不会继承文件描述符三,因此 command1 获得一个“更干净”、更标准的环境。 您还可以将内部
4>&-
移到 3>&-
旁边,但我想为什么不尽可能限制其范围。
几乎没有程序直接使用预打开的文件描述符三和四,因此您几乎永远不必担心它,但最好记住后者并将其用于通用情况。
我还在寻找一种可以在 AppleScript 中的 do shell script
中工作的单行代码,它始终使用 /bin/sh (由 zsh 模拟)。 这个版本是我发现的唯一一个运行良好的版本:
mycommand 2>&1 | tee -a output.log; exit ${PIPESTATUS[0]}
或者在 AppleScript 中
set theResult to do shell script "mycommand 2>&1 | tee -a " & quoted form of logFilePath & "; exit ${PIPESTATUS[0]}"
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
这适用于 Bash:
括号将管道故障的影响限制为仅一个命令。
从 bash(1) 手册页:
This works with Bash:
The parentheses are there to limit the effect of pipefail to just the one command.
From the bash(1) man page: