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

Men*_*hak 66 bash ubuntu tee process-substitution

我有一个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)
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,我调用rsync几次,我想看到文件名在处理时,但最后我还是想在变量中输出,因为我稍后会解析它.

是否有一种"更清洁"的方式来实现这一目标?

如果它有所作为,我使用的是Ubuntu 12.04,bash 4.2.24.

Op *_*kel 73

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

exec 5>&1
FF=$(echo aaa|tee >(cat - >&5))
echo $FF
Run Code Online (Sandbox Code Playgroud)

将打印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
Run Code Online (Sandbox Code Playgroud)

  • 怎么样`FF = $(echo aaa | tee/dev/tty)` [来源](http://stackoverflow.com/a/7902174/4725840) (5认同)
  • 描述符不应该在父shell中关闭吗? (3认同)
  • @akhan:我认为那是'exec 5>& - `然后呢? (2认同)
  • @user1011471,当您编写 `tee >(inner_cmd...)` 时,shell 会打开一个通向内部命令的管道,这样管道的写入端也由外部命令 (tee) 继承,通常在描述符 63 下。外部命令不知道 shell 做了什么有趣的事情,它获取一个文件名参数“/dev/fd/63”。https://en.wikipedia.org/wiki/Process_substitution 不知道为什么会出现此错误,但它与写入描述符 5 的内部命令无关。 (2认同)

Rus*_*vis 35

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

exec 5>&1
FF=$(echo aaa|tee /dev/fd/5)
echo $FF
Run Code Online (Sandbox Code Playgroud)

  • 我一直在想你是否可以使用`tee/dev/fd/1`,但这不起作用,因为输出仍然被`$()`捕获.因此,如果其他人想知道同样的事情,那么必须使用额外的文件描述符(如5). (9认同)
  • / dev/fd/5不会特定于操作系统吗? (6认同)
  • 我们可以进一步简化,而无需使用`exec`使其成为一体:`{FF = $(echo aaa | tee / dev / fd / 5); } 5>&1`花括号允许重定向在运行subshel​​l命令之前发生,而`$ FF`仍保留在当前shell的范围内(不适用于普通括号`(``)`。这样,以后甚至无需关闭FD 5,这是一个被忽视的卫生习惯。 (2认同)
  • @akhan不,不会,据说如果操作系统本身不存在这条路径,bash就会模拟这条路径。 (2认同)

Bry*_*ach 12

这是捕获两者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"
Run Code Online (Sandbox Code Playgroud)

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

如果省略2>&1则只会stdout被捕获.


Pio*_*das 5

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

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

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

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


tri*_*eee 5

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

variable=$(command with options | tee /dev/tty)
Run Code Online (Sandbox Code Playgroud)

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