Search code examples
bashoutput-redirect

In bash, how to capture some output in variable, letting rest got to standard output


I'm aware of the bash "capture output" capability, along the lines of (two separate files):

sub.sh:     echo hello
main.sh:    greeting="$(./sub.sh)"

This will set the greeting variable to be hello.

However, I need to write a script where I want to just capture some information, allowing the rest to got to "normal" standard output:

sub.sh:     xyzzy hello ; plugh goodbye
main.sh:    greeting="$(./sub.sh)"

What I would like is for hello to be placed in the greeting variable but goodbye to be sent to the standard output of main.sh.

What do the magic commands xyzzy and plugh need to be replaced with above (or what can I do in main.sh), in order to achieve this behaviour? I suspect it could be done with some sneaky fiddling around of handle-based redirections but I'm not sure. If not possible, I'll have to revert to writing one of the items to a temporary file to be picked up later, but I'd prefer not to do that.


To make things clearer, here's the test case I'm using (currently using the non-working file handle 3 method). First sub.sh:

echo xx_greeting >&3 # This should be captured to variable.
echo xx_stdout       # This should show up on stdout.
echo xx_stderr >&2   # This should show up on stderr.

Then main.sh:

greeting="$(./sub.sh)" 3>&1
echo "Greeting was ${greeting}"

And I run it thus:

./main.sh >/tmp/out 2>/tmp.err

expecting to see the following files:

/tmp/out:
    xx_stdout
    Greeting was xx_greeting 
/tmp/err:
    xx_stderr

Solution

  • This can be done by introducing an extra file descriptor as follows. First the sub.sh script for testing, which simply writes a different thing to three different descriptors (implicit >&1 for the first):

    echo for-var
    echo for-out >&3
    echo for-err >&2
    

    Second, the main.sh which calls it:

    exec 3>&1
    greeting="$(./sub.sh)"
    echo "Variable is ${greeting}"
    

    Then you simply run it ensuring you know what output is going to the different locations:

    pax> ./main.sh > xxout 2> xxerr
    
    pax> cat xxout
    for-out
    Variable is for-var
    
    pax> cat xxerr
    for-err
    

    Hence you can see that, when calling sub.sh from main.sh, stuff written to file handle 1 goes to the capture variable, stuff written to file handle 2 goes to standard error, and stuff written to file handle 3 goes to standard output.