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

Ken*_*war 10 ksh shell-script

考虑源代码:

1. 父.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}"
Run Code Online (Sandbox Code Playgroud)

2. 子.sh

#!/usr/bin/ksh
...
exit 1;
Run Code Online (Sandbox Code Playgroud)

输出:

Exit status: 1
Exit status: 0
Run Code Online (Sandbox Code Playgroud)
  • 变量$exit_status正在捕获 Child.sh 的退出状态,1.
  • 在第二种情况下,$exit_status正在捕获 tee 的退出状态,即0.

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

Sté*_*las 17

comp.unix.shell FAQ复制(并改进)(因为我碰巧写了 FAQ 的那部分):

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

首先,请注意 cmd1 退出代码可能不为零,但仍然不意味着错误。这发生在例如

cmd | head -n 1
Run Code Online (Sandbox Code Playgroud)

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

了解管道元素的退出状态

cmd1 | cmd2 | cmd3
Run Code Online (Sandbox Code Playgroud)

使用 zsh(和fish 3.1+):

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

使用 bash:

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

与任何其他类似 Bourne 的贝壳

您需要使用一个技巧将退出代码传递给主 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"
Run Code Online (Sandbox Code Playgroud)

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

使用 POSIX 外壳

您可以使用此功能使其更容易:

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 Code Online (Sandbox Code Playgroud)

将其用作:

run cmd1 \| cmd2 \| cmd3
Run Code Online (Sandbox Code Playgroud)

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