“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)
这适用于 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:
我在 Capture Exit Code using Pipe & 中偶然发现了一些有趣的解决方案。 T恤。
Bash 中有可用的 $PIPESTATUS 变量:
<前><代码>假 | 三通/dev/null
[ $PIPESTATUS -eq 0 ] || 退出$PIPESTATUS
Perl 中“eet”的最简单原型可能如下所示:
I stumbled upon a couple of interesting solutions at Capture Exit Code Using Pipe & Tee.
There is the $PIPESTATUS variable available in Bash:
And the simplest prototype of "eet" in Perl may look as follows:
这是一个
eet
。 适用于我能接触到的所有 Bash,从 2.05b 到 4.0。(
pipefail
和$PIPESTATUS
很好,但我记得它们是在 3.1 左右引入的。)Here's an
eet
. Works with every Bash I can get my hands on, from 2.05b to 4.0.(
pipefail
and$PIPESTATUS
are nice, but I recall them being introduced in 3.1 or thereabouts.)这是我认为最好的纯 Bourne-shell 解决方案,可用作构建“eet”的基础:
我认为这是从内到外的最佳解释 -
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 ,所以为了更卫生,我们会这样做:命令从启动它们的进程继承文件描述符,因此整个第二行将继承文件描述符 4,复合命令后跟
3>&1
将继承文件描述符三。 因此4>&-
确保内部复合命令不会继承文件描述符 4,而3>&-
确保command1< /code> 不会继承文件描述符三,因此 command1 获得一个“更干净”、更标准的环境。 您还可以将内部
4>&-
移到3>&-
旁边,但我想为什么不尽可能限制其范围。几乎没有程序直接使用预打开的文件描述符三和四,因此您几乎永远不必担心它,但最好记住后者并将其用于通用情况。
This is what I consider to be the best pure-Bourne-shell solution to use as the base upon which you could build your "eet":
I think this is best explained from the inside out –
command1
will execute and print its regular output on stdout (file descriptor 1), then once it's done,echo
will execute and printcommand1
's exit code on its stdout, but that stdout is redirected to file descriptor three.While
command1
is running, its stdout is being piped to command2 (echo
's output never makes it to command2 because we send it to file descriptor 3 instead of 1, which is what the pipe reads). Then we redirectcommand2
's output to file descriptor 4, so that it also stays out of file descriptor one – because we want file descriptor one clear for when we bring theecho
output on file descriptor three back down into file descriptor one so that the command substitution (the backticks) can capture it.The final bit of magic is that first
exec 4>&1
we did as a separate command – it opens file descriptor four as a copy of the external shell's stdout. Command substitution will capture whatever is written on standard out from the perspective of the commands inside it – but, sincecommand2
's output is going to file descriptor four as far as the command substitution is concerned, the command substitution doesn't capture it – however, once it gets "out" of the command substitution, it is effectively still going to the script's overall file descriptor one.(The
exec 4>&1
has to be a separate command to work with many common shells. In some shells it works if you just put it on the same line as the variable assignment, after the closing backtick of the substitution.)(I use compound commands (
{ ... }
) in my example, but subshells (( ... )
) would also work. The subshell will just cause a redundant forking and awaiting of a child process, since each side of a pipe and the inside of a command substitution already normally implies a fork and await of a child process, and I don't know of any shell being coded to recognize that it can skip one of those forks because it's already done or is about to do the other.)You can look at it in a less technical and more playful way, as if the outputs of the commands are leapfrogging each other:
command1
pipes tocommand2
, then theecho
's output jumps overcommand2
so thatcommand2
doesn't catch it, and thencommand2
's output jumps over and out of the command substitution just asecho
lands just in time to get captured by the substitution so that it ends up in the variable, andcommand2
's output goes on its way to the standard output, just as in a normal pipe.Also, as I understand it, at the end of this command,
$?
will still contain the return code of the second command in the pipe, because variable assignments, command substitutions, and compound commands are all effectively transparent to the return code of the command inside them, so the return status ofcommand2
should get propagated out.A caveat is that it is possible that
command1
will at some point end up using file descriptors three or four, or thatcommand2
or any of the later commands will use file descriptor four, so to be more hygienic, we would do:Commands inherit file descriptors from the process that launches them, so the entire second line will inherit file descriptor four, and the compound command followed by
3>&1
will inherit the file descriptor three. So the4>&-
makes sure that the inner compound command will not inherit file descriptor four, and the3>&-
makes sure thatcommand1
will not inherit file descriptor three, so command1 gets a 'cleaner', more standard environment. You could also move the inner4>&-
next to the3>&-
, but I figure why not just limit its scope as much as possible.Almost no programs uses pre-opened file descriptor three and four directly, so you almost never have to worry about it, but the latter is probably best to keep in mind and use for general-purpose cases.
KornShell,全部都在一行中:
KornShell, all in one line:
我还在寻找一种可以在 AppleScript 中的
do shell script
中工作的单行代码,它始终使用 /bin/sh (由 zsh 模拟)。 这个版本是我发现的唯一一个运行良好的版本:或者在 AppleScript 中
I was also looking for a one-liner that works in
do shell script
in AppleScript, which always uses /bin/sh (emulated by zsh). This version is the only one I found that works well:or in AppleScript
希望这对你有用。
Hopefully this works for you.
假设使用 Bash 或 Z shell (
zsh
),请注意,标准错误的重定向和复制到标准输出的顺序非常重要!
我没有意识到您也想在屏幕上看到输出。 这当然会将所有输出定向到文件 my_log。
Assuming Bash or Z shell (
zsh
),N.B. The sequence of redirection and duplication of standard error onto standard output is significant!
I didn't realise you wanted to see the output on screen as well. This will of course direct all output to the file my_log.