85

我有一个 bash 脚本,它调用了几个长时间运行的进程。出于处理原因,我想将这些调用的输出捕获到变量中。但是,因为这些是长时间运行的进程,我希望 rsync 调用的输出实时显示在控制台中,而不是事后显示。

为此,我找到了一种方法,但它依赖于将文本输出到 /dev/stderr。我觉得输出到 /dev/stderr 不是一个好的做事方式。

VAR1=$(for i in {1..5}; do sleep 1; echo $i; done | tee /dev/stderr)

VAR2=$(rsync -r -t --out-format='%n%L' --delete -s /path/source1/ /path/target1 | tee /dev/stderr)

VAR3=$(rsync -r -t --out-format='%n%L' --delete -s /path/source2/ /path/target2 | tee /dev/stderr)

在上面的示例中,我调用了 rsync 几次,我想在处理文件名时查看它们,但最后我仍然希望将输出放在变量中,因为稍后我将对其进行解析。

有没有一种“更清洁”的方式来实现这一点?

如果有什么不同,我使用的是 Ubuntu 12.04,bash 4.2.24。

4

5 回答 5

90

在您的 shell 中复制 &1(在我的示例中为 5)并在子 shell 中使用 &5(这样您将写入父 shell 的标准输出(&1)):

exec 5>&1
FF=$(echo aaa|tee >(cat - >&5))
echo $FF

将打印 aaa 两次,因为子外壳中的 echo,第二次打印变量的值。

在您的代码中:

exec 5>&1
VAR1=$(for i in {1..5}; do sleep 1; echo $i; done | tee >(cat - >&5))
# use the value of VAR1
于 2012-09-16T22:58:56.983 回答
42

Op De Cirkel 的回答是正确的。它可以进一步简化(避免使用cat):

exec 5>&1
FF=$(echo aaa|tee /dev/fd/5)
echo $FF
于 2013-04-30T04:25:38.203 回答
19

这是一个捕获stderr命令的退出代码的示例。这是建立在罗素戴维斯的答案之上的。

exec 5>&1
FF=$(ls /taco/ 2>&1 |tee /dev/fd/5; exit ${PIPESTATUS[0]})
exit_code=$?
echo "$FF"
echo "Exit Code: $exit_code"

如果文件夹/taco/存在,这将捕获其内容。如果该文件夹不存在,它将捕获一条错误消息,退出代码将为 2。

如果您省略2>&1then onlystdout将被捕获。

于 2017-01-30T19:18:28.277 回答
7

如果“控制台”是指您当前的 TTY,请尝试

variable=$(command with options | tee /dev/tty)

这是一个有点可疑的做法,因为尝试使用它的人有时会在没有 TTY(cron 作业等)时输出到达意想不到的地方时感到惊讶。

于 2018-11-16T12:50:16.813 回答
6

您可以使用三个以上的文件描述符。在这里试试:

http://tldp.org/LDP/abs/html/io-redirection.html

“每个打开的文件都被分配一个文件描述符。[2] stdin、stdout 和 stderr 的文件描述符分别为 0、1 和 2。对于打开其他文件,保留描述符 3 到 9。有时有用将这些附加文件描述符之一分配给 stdin、stdout 或 stderr 作为临时重复链接。”

关键是为了达到这个结果是否值得让脚本更复杂。其实这并没有错,你这样做的方式。

于 2012-09-16T22:41:35.117 回答