bash 将标准输出捕获到变量但仍将其显示在控制台中
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12451278/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Capture stdout to a variable but still display it in the console
提问by Mendhak
I have a bash script which calls several long-running processes. I want to capture the output of those calls into variables for processing reasons. However, because these are long running processes, I would like the output of the rsync calls to be displayed in the console in real-timeand not after the fact.
我有一个 bash 脚本,它调用了几个长时间运行的进程。出于处理原因,我想将这些调用的输出捕获到变量中。但是,因为这些是长时间运行的进程,我希望 rsync 调用的输出实时显示在控制台中,而不是事后显示。
To this end, I have founda way of doing it but it relies on outputting the text to /dev/stderr. I feel that outputting to /dev/stderr is not a good way of doing things.
为此,我找到了一种方法,但它依赖于将文本输出到 /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)
In the example above, I am calling rsync a few times and I want to see the file names as they are processed, but in the end I still want the output in a variable because I will be parsing it later.
在上面的示例中,我多次调用 rsync 并且我想在处理文件时查看文件名,但最后我仍然希望输出在一个变量中,因为我稍后会对其进行解析。
Is there a 'cleaner' way of accomplishing this?
有没有一种“更干净”的方法来实现这一目标?
If it makes a difference, I am using Ubuntu 12.04, bash 4.2.24.
如果它有所不同,我使用的是 Ubuntu 12.04,bash 4.2.24。
回答by Op De Cirkel
Duplicate &1 in your shell (in my examle to 5) and use &5 in the subshell (so that you will write to stdout (&1) of the parent shell):
在您的 shell 中复制 &1(在我的示例中为 5)并在子 shell 中使用 &5(以便您将写入父 shell 的 stdout(&1)):
exec 5>&1
FF=$(echo aaa|tee >(cat - >&5))
echo $FF
Will print aaa two times, ones because of the echo in the subshell, and second time print the value of the variable.
将打印 aaa 两次,一次是因为子shell 中的回声,第二次打印变量的值。
In your code:
在您的代码中:
exec 5>&1
VAR1=$(for i in {1..5}; do sleep 1; echo $i; done | tee >(cat - >&5))
# use the value of VAR1
回答by Russell Davis
Op De Cirkel's answer has the right idea. It can be simplified even more (avoiding use of cat
):
Op De Cirkel 的回答是正确的。它可以进一步简化(避免使用cat
):
exec 5>&1
FF=$(echo aaa|tee /dev/fd/5)
echo $FF
回答by Bryan Roach
Here's an example capturing both stderr
and the command's exit code. This is building on the answer by Russell Davis.
这是一个同时捕获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"
If the folder /taco/
exists, this will capture its contents. If the folder doesn't exist, it will capture an error message and the exit code will be 2.
如果文件夹/taco/
存在,这将捕获其内容。如果文件夹不存在,它将捕获一条错误消息,退出代码将为 2。
If you omit 2>&1
then only stdout
will be captured.
如果省略,2>&1
则只会stdout
被捕获。
回答by tripleee
If by "the console" you mean your current TTY, try
如果“控制台”是指您当前的 TTY,请尝试
variable=$(command with options | tee /dev/tty)
This is slightly dubious practice because people who try to use this sometimes are surprised when the output lands somewhere unexpected when they don't have a TTY (cron jobs etc).
这是一个有点可疑的做法,因为当他们没有 TTY(cron 作业等)时,尝试使用它的人有时会在输出出现意外时感到惊讶。
回答by Piotr Wadas
You can use more than three file descriptors. Try here:
您可以使用三个以上的文件描述符。在这里尝试:
http://tldp.org/LDP/abs/html/io-redirection.html
http://tldp.org/LDP/abs/html/io-redirection.html
"Each open file gets assigned a file descriptor. [2] The file descriptors for stdin, stdout, and stderr are 0, 1, and 2, respectively. For opening additional files, there remain descriptors 3 to 9. It is sometimes useful to assign one of these additional file descriptors to stdin, stdout, or stderr as a temporary duplicate link."
“每个打开的文件都被分配了一个文件描述符。[2] stdin、stdout 和 stderr 的文件描述符分别为 0、1 和 2。对于打开其他文件,保留描述符 3 到 9。有时这很有用将这些附加文件描述符之一作为临时重复链接分配给 stdin、stdout 或 stderr。”
The point is whether it's worth to make script more complicated just to achieve this result. Actually it's not really wrong, the way you do it.
关键是为了达到这个结果是否值得让脚本变得更复杂。其实这并没有错,看你怎么做。
回答by leondepeon
Alternative to using /dev/tty
, or an additional file descriptor as suggested by the other answers, you can also flip it and simply use a temp file. This is arguably easier to read and more portable in certain situations.
/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