Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Capture stdout to a variable but still display it in the console

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-time and not after the fact.

To this end, I have found a 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.

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.

Is there a 'cleaner' way of accomplishing this?

If it makes a difference, I am using Ubuntu 12.04, bash 4.2.24.

like image 569
Mendhak Avatar asked Sep 16 '12 22:09

Mendhak


People also ask

How do you store stdout in a variable?

To store the output of a command in a variable, you can use the shell command substitution feature in the forms below: variable_name=$(command) variable_name=$(command [option ...] arg1 arg2 ...) OR variable_name='command' variable_name='command [option ...]

How do you capture stdout?

To capture a tool's standard output stream, add the stdout field with the name of the file where the output stream should go. Then add type: stdout on the corresponding output parameter.

How do you capture output of a command in a variable in a shell script?

Here are the different ways to store the output of a command in shell script. You can also use these commands on terminal to store command outputs in shell variables. variable_name=$(command) variable_name=$(command [option ...] arg1 arg2 ...) OR variable_name=`command` variable_name=`command [option ...]

How do you echo a new line?

There are a couple of different ways we can print a newline character. The most common way is to use the echo command. However, the printf command also works fine. Using the backslash character for newline “\n” is the conventional way.

Is it possible to capture stdout of out output in shell script?

Next, the stdout of out is assigned to y, and the redirected stderr is captured by x, without the usual loss of y to a command substitution's subshell. It isn't possible in other shells, because all constructs which capture output require putting the producer into a subshell, which in this case, would include the assignment.

Why should I Capture stdout/stderr output by default?

One primary benefit of the default capturing of stdout/stderr output is that you can use print statements for debugging: and running this module will show you precisely the output of the failing function and hide the other one:

How do you capture a stderr in a command substitution?

Stderr of the inner command group is redirected to stdout (so that it applies to the inner substitution). Next, the stdout of out is assigned to y, and the redirected stderr is captured by x, without the usual loss of y to a command substitution's subshell.

How does catch () remove stderr and stdout?

However it nearly immediately removes these, such that they are only around for a very short time. catch_1 () catches stdout (FD 1) into a variable and moves stderr to stdout, such that the next ("left") catch_1 can catch that. Processing in catch is done from right to left, so the left catch_1 is executed last and catches stderr.


2 Answers

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):

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.

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 
like image 164
Op De Cirkel Avatar answered Oct 02 '22 19:10

Op De Cirkel


Op De Cirkel's answer has the right idea. It can be simplified even more (avoiding use of cat):

exec 5>&1 FF=$(echo aaa|tee /dev/fd/5) echo $FF 
like image 42
Russell Davis Avatar answered Oct 02 '22 20:10

Russell Davis