Unix T形三通和退出状态

Unix T形三通和退出状态,unix,shell,tee,Unix,Shell,Tee,是否有一种替代“tee”的方法,它捕获正在执行的命令的STDOUT/STDERR,并以与已处理命令相同的退出状态退出。如下所示: eet -a some.log -- mycommand --foo --bar 其中“eet”是“tee”的一个虚构替代品:(-a means append,--分隔捕获的命令)破解这样一个命令应该不难,但可能它已经存在,而我不知道它 谢谢。G'day 假设bash或zsh my_command >>my_log 2>&1 注意:将ST

是否有一种替代“tee”的方法,它捕获正在执行的命令的STDOUT/STDERR,并以与已处理命令相同的退出状态退出。如下所示:

eet -a some.log -- mycommand --foo --bar
其中“eet”是“tee”的一个虚构替代品:(-a means append,--分隔捕获的命令)破解这样一个命令应该不难,但可能它已经存在,而我不知道它

谢谢。

G'day

假设bash或zsh

my_command >>my_log 2>&1
注意:将STDERR重定向和复制到STDOUT的顺序非常重要

编辑:Oops。没有意识到您也想在屏幕上看到输出。这当然会将所有输出定向到文件my_log


干杯,

在这里偶然发现了几个有趣的解决方案

1) bash中有$PIPESTATUS变量可用:

   false | tee /dev/null
   [ $PIPESTATUS -eq 0 ] || exit $PIPESTATUS
2) perl中最简单的“eet”原型如下所示:

   open MAKE, "command 2>&1 |" or die;
   open (LOGFILE, ">>some.log") or die;
   while (<MAKE>) { print LOGFILE $_; print }
   close MAKE; # to get $?
   my $exit = $? >> 8;
   close LOGFILE;
打开MAKE,“command 2>&1 |”或die;
打开(日志文件“>>some.log”)或死亡;
while(){print LOGFILE$\ print}
关闭MAKE;#要得到美元?
我的$exit=$?>>8.
关闭日志文件;
希望这对您有效。

这对bash有效:

{ mycommand --foo --bar 2>&1; ret=$?; } | tee -a some.log; (exit $ret)
(
  set -o pipefail
  mycommand --foo --bar | tee some.log
)
括号用于限制pipefail的效果,使其仅限于一个命令

从bash(1)手册页:

管道的返回状态是最后一个命令的退出状态,除非启用了
pipefail
选项。如果启用了
pipefail
,则管道的返回状态是最后一个(最右边)以非零状态退出的命令的值,如果所有命令都成功退出,则返回状态为零。
这是一个
eet
。从2.05b到4.0,每一个Bash都可以使用

#!/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版或大约3.1版中引入的。)

Korn shell,全部在一行中:

foo; RET_VAL=$?; if test ${RET_VAL} != 0;then echo $RET_VAL; echo Error occurred!>/tmp/out.err;exit 2;fi |tee >>/tmp/out.err ; if test ${RET_VAL} != 0;then exit $RET_VAL;fi

这是我认为是最好的纯Burne shell解决方案,作为你可以建立你的“EET”的基础:

我认为最好从内到外解释这一点——command1将在stdout(文件描述符1)上执行并打印其常规输出,然后一旦完成,printf将在其stdout上执行并打印command1的退出代码,但stdout将重定向到文件描述符3

当command1运行时,它的stdout被管道传输到command2(printf的输出永远不会发送到command2,因为我们将它发送到文件描述符3,而不是管道读取的1)。然后我们将command2的输出重定向到文件描述符4,这样它也不会出现在文件描述符1中–因为我们希望文件描述符1在稍后的一段时间内可以自由使用,因为我们会将文件描述符3上的printf输出返回到文件描述符1中–因为这就是命令替换(backticks)的内容,将捕获,这就是将被放入变量中的内容

最后一点神奇之处在于,我们首先作为一个单独的命令执行了
exec4>&1
,它将文件描述符4作为外部shell标准输出的副本打开。从内部命令的角度来看,命令替换将捕获在标准输出上写入的任何内容–但是,由于就命令替换而言,command2的输出将进入文件描述符4,因此命令替换不会捕获它–但是,一旦它从命令替换中“取出”后,它实际上仍然是脚本的整体文件描述符1

(exec 4>&1必须是一个单独的命令,因为当您试图在命令替换中写入文件描述符时,许多常见的shell都不喜欢它,而命令替换是在使用替换的“外部”命令中打开的。因此,这是最简单的可移植方法。)

你可以用一种不那么技术化、更有趣的方式来看待它,就好像命令的输出是相互跳跃的:command1将管道连接到command2,然后printf的输出跳过command2,这样command2就不会捕捉到它,然后,命令2的输出跳出命令替换,就像printf及时到达并被替换捕获一样,因此它最终进入变量,而命令2的输出以其愉快的方式写入标准输出,就像在普通管道中一样

另外,据我所知,
$?
仍将在管道中包含第二个命令的返回代码,因为变量赋值、命令替换和复合命令对其中命令的返回代码都是有效透明的,因此command2的返回状态应该传播出去

需要注意的是,command1可能会在某个时候使用文件描述符3或4,或者command2或任何更高版本的命令都会使用文件描述符4,因此为了更健壮,您可以执行以下操作:

exec 4>&1
exitstatus=`{ { command1 3>&-; printf $? 1>&3; } 4>&- | command2 1>&4; } 3>&1`
exec 4>&-
注意,在我的示例中我使用复合命令,但是子shell(使用
()
而不是
{}
也可以工作,尽管可能效率较低。)

命令从启动它们的进程继承文件描述符,因此整个第二行将继承文件描述符四,后面跟着
3>&1
的复合命令将继承文件描述符三。因此,
4>&-
确保内部复合命令不会继承文件描述符4,而
3>&-
不会继承文件描述符3,因此command1获得了一个“更干净、更标准的环境”。您也可以将内部
4>和-
移动到
3>和-
旁边,但我想为什么不尽可能地限制其范围

我不确定直接使用文件描述符3和文件描述符4的频率——我认为大多数情况下程序使用的系统调用返回的不是当前使用的文件描述符,但有时代码直接写入文件描述符3,我猜(我可以想象一个程序检查一个文件描述符是否打开,如果打开就使用它,o
# You want to pipe command1 through command2:
exec 4>&1
exitstatus=`{ { command1; printf $? 1>&3; } | command2 1>&4; } 3>&1`
# $exitstatus now has command1's exit status.
exec 4>&1
exitstatus=`{ { command1 3>&-; printf $? 1>&3; } 4>&- | command2 1>&4; } 3>&1`
exec 4>&-
exitstatus=`{ 3>&- command1; } 1>&3; printf $?` 3>&1 | command2
# $exitstatus now has command1's exit status.
commandA | command2
commandB 3>&1
VAR_FOO=`commandC`
commandD ; printf $?
commandE 1>&3
{ 3>&- command1; }