将stdout捕获到变量但仍在控制台中显示它

时间:2012-09-16 22:30:22

标签: bash ubuntu tee process-substitution

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

为此,我有found方法,但它依赖于将文本输出到/ 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。

6 个答案:

答案 0 :(得分:72)

在shell中复制& 1(在我的考试中为5)并在子shell中使用& 5(这样你就可以写入父shell的stdout(& 1)):

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

将打印aaa两次,因为子shell中的回声,并且第二次打印变量的值。

在您的代码中:

exec 5>&1
VAR1=$(for i in {1..5}; do sleep 1; echo $i; done | tee >(cat - >&5))
# use the value of VAR1

答案 1 :(得分:34)

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

exec 5>&1
FF=$(echo aaa|tee /dev/fd/5)
echo $FF

答案 2 :(得分:11)

这是捕获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>&1,则只会捕获stdout

答案 3 :(得分:5)

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

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

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

重点是,为了达到这个结果,是否值得使脚本更复杂。实际上,你做的方式并没有错。

答案 4 :(得分:3)

如果通过“控制台”表示您当前的TTY,请尝试

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

这是一种有点可疑的做法,因为尝试使用此方法的人有时会在没有TTY的情况下(临时工作等)将输出降到意外的地方时感到惊讶。

答案 5 :(得分:0)

除了使用/dev/tty或其他答案建议的其他文件描述符之外,您还可以翻转它并仅使用临时文件。可以说,在某些情况下,这更容易阅读,也更便于移植。

tmpFile=$(mktemp)  # mak-a de temp
rsync /a /b | tee $tmpFile # sync my b*tch up
if grep "U F'd up" $tmpFile; then
  rm -rf / #Seppuku
fi