Bash:Eval & Tee:如何进行输出、捕获的输出和错误检查

Bash:Eval & Tee:如何进行输出、捕获的输出和错误检查

在 Ubuntu 16.04 下使用#!/bin/bash我想要一个 eval'ed 命令...

  • 提供直接输出
  • 将输出获取到变量
  • 检查评估结果
# valid command
foo=$(eval "ls" | tee /dev/tty);
echo ${PIPESTATUS[@]}
echo $foo

# invalid command
foo=$(eval "ls -ßnonsense" | tee /dev/tty);
echo ${PIPESTATUS[@]}
echo $foo

PIPESTATUS我的问题是,当我得到一个有意义的

  • 我完全删除了tee /dev/tty(否则,是的,它掩盖了作为第一个管道命令一部分的任何评估失败)
  • 或者将其放在括号之外foo=$(eval "ls" ) | tee /dev/tty;- ...但在这种情况下,我不再得到直接输出或 $foo 输出。

答案1

改用zsh

shell_code='ls' # or ls -ßnonsense...
{ foo=$(eval " $shell_code" >&1 >&3 3>&-); } 3>&1
print -r status=$? output=$foo

在这里,我们将原始 stdout (如果该脚本从终端未重定向地运行,则仅是控制 tty)复制到 fd 3,并将eval的输出重定向到命令替换和 fd 3 (使用 zsh 自己的teeing当 fd 多次重定向以进行输出时的机制)。

在带有 的系统上使用和其他支持(但如果在 Linux/Cygwin 上则不支持 ksh93)bash的 shell ,您可以执行类似的操作:pipefail/dev/fd/<n>

shell_code='ls' # or ls -ßnonsense...
{
  foo=$(
    set -o pipefail
    eval " $shell_code" 3>&- | 
      tee 4>&1 >&3 3>&- /dev/fd/4
  )
} 3>&1

printf 'status=%s output=%s\n' "$?" "$foo"

打开pipefail时,管道的退出状态是管道中失败的最右边命令的退出状态。所以这里将是eval除非tee失败。

eval要无条件获取退出状态,bash可以使用 :

shell_code='ls' # or ls -ßnonsense...
{
  foo=$(
    eval " $shell_code" 3>&- |
      tee 4>&1 >&3 3>&- /dev/fd/4
    exit "$PIPESTATUS"
  )
} 3>&1

printf 'status=%s output=%s\n' "$?" "$foo"

在这些方法中,tee的 stdout 是原始的 stdout,而命令替换管道通过 提供/dev/fd/4。我们正在这样做,而不是相反(tee将 stdout 发送到命令替换管道,通过某些命令写入原始 stdout /dev/fd/<n>),尽管这会使代码变得更加复杂,但要解决后者不起作用的事实在 Linux 或 Cygwin 系统上,打开/dev/fd/<n>与复制 fd 不同<n>(但对于指向管道写入端的 fd 执行此操作,就像命令替换一样(不是使用套接字对的 ksh93)在功能上是等效的)

答案2

这是一个可移植的解决方案,应该适用于大多数(如果不是全部)Bourne 语法 shell;使用bashdashksh93ksh88ash、 (Bourne)sh和进行测试zsh

for command in "ls -ßnonsense" "ls -ld /tmp"; do
    foo=`{ eval "$command"; echo $? > /tmp/ps.$$; } | tee /dev/tty;`
    st=`cat /tmp/ps.$$;rm /tmp/ps.$$`
    echo "foo=$foo status=$st"
done

相关内容