如何在 korn shell 中捕获返回状态并同时使用 tee?

如何在 korn shell 中捕获返回状态并同时使用 tee?

考虑源代码:

1.Parent.sh

#!/usr/bin/ksh
# No tee
ksh Child.sh;
exit_status=$?;
echo "Exit status: ${exit_status}"
# Using tee
ksh Child.sh | tee -a log.txt;
exit_status=$?;
echo "Exit status: ${exit_status}"

2.Child.sh

#!/usr/bin/ksh
...
exit 1;

输出:

Exit status: 1
Exit status: 0

  • 变量$exit_status正在捕获 Child.sh 的退出状态,因此也是如此1
  • 在第二种情况下,$exit_status正在捕获 tee 的退出状态,即0

那么如何捕获退出状态并使用 tee 呢?

答案1

转载(并改进)自comp.unix.shell 常见问题解答(因为我碰巧写了常见问题解答的这一部分):

如何在cmd1|cmd2中获取cmd1的退出代码

首先,请注意 cmd1 退出代码可能非零,但并不意味着错误。例如,这种情况发生在

cmd | head -n 1

您可能会观察到 141(或 ksh93 为 269,yash 为 397)退出状态cmd,但这是因为在读取一行后终止cmd时被 SIGPIPE 信号中断 。head -n 1

了解管道元素的退出状态

cmd1 | cmd2 | cmd3

使用 zsh(和 Fish 3.1+):

退出代码在特殊数组中提供pipestatuscmd1退出代码在 中$pipestatus[1]cmd3退出代码在 中 $pipestatus[3],因此$status/$?始终与 相同 $pipestatus[-1]

与bash:

退出代码在特殊数组中提供PIPESTATUScmd1退出代码位于 中${PIPESTATUS[0]}cmd3退出代码位于 中 ${PIPESTATUS[2]},因此始终与(或对于 4.2 之前的版本)$?相同 。${PIPESTATUS[-1]}${PIPESTATUS[@]: -1}

与任何其他类似 Bourne 的 shell

您需要使用一种技巧将退出代码传递到主 shell。您可以使用管道(2) 来完成此操作。您不是运行,而是cmd1运行cmd1; echo "$?"并确保 $?进入外壳。

exec 3>&1
code=`
  # now, inside the backticks, fd4 goes to the pipe
  # whose other end is read and stored in $code  for
  # later evaluation; fd1 is the normal standard output
  # preserved the line before with exec 3>&1

  exec 4>&1 >&3 3>&- 
  {
    cmd1 4>&-; echo "ec1=$?;" >&4
  } | {
    cmd2 4>&-; echo "ec2=$?;" >&4
  } | cmd3 4>&-
  echo "ec3=$?;" >&4
`
exec 3>&-
eval "$code"

$ec1$ec2、中的退出代码$ec3

使用 POSIX shell

您可以使用此功能来简化操作:

run() {
  j=1
  while eval "\${pipestatus_$j+:} false"; do
    unset "pipestatus_$j"
    j=$(($j+1))
  done
  j=1 com= k=1 l=
  for arg do
    case $arg in
      ('|')
        com="$com {
               $l "'3>&-
               echo "pipestatus_'$j'=$?" >&3
             } 4>&- |'
        j=$(($j+1)) l=;;
      (*)
        l="$l \"\${$k}\""
    esac
    k=$(($k+1))
  done
  com="$com $l"' 3>&- >&4 4>&-
       echo "pipestatus_'$j'=$?"'

  { eval "$(exec 3>&1; eval "$com")"; } 4>&1
  j=1
  ret=0
  while eval "\${pipestatus_$j+:} false"; do
    eval '[ "$pipestatus_'"$j"'" -eq 0 ] || ret=$pipestatus_'"$j"
    j=$(($j+1))
  done
  return "$ret"
}

将其用作:

run cmd1 \| cmd2 \| cmd3

退出代码位于$pipestatus_1、 、中$pipestatus_2$pipestatus_3并且$?是最右侧的非零退出状态(与pipefail某些 shell 的选项类似)。

答案2

您可以使用 && 和 ||在 bash 中执行此操作 - 我认为类似的操作在 ksh 中也能工作。

ksh Child.sh && exit_status="$?" || exit_status="$?" | tee -a log.txt;

编辑:

正如@Stephane指出的,A && B | C将输出A到stdout,并且仅通过管道将B传送到C。它需要对输出进行分组,将它们通过管道连接在一起。您无法将变量从子 shell 传递到调用方,但是,您可以这样做:

x=$(tempfile) && exit_status=$(ksh Child.sh > $x; echo $?) && (cat $x; rm $x) | tee -a log.txt

相关内容