Differences between revisions 13 and 18 (spanning 5 versions)
Revision 13 as of 2008-11-28 23:22:21
Size: 3142
Editor: GreyCat
Comment: fix up language, add note on original question text
Revision 18 as of 2011-06-22 02:35:26
Size: 3838
Editor: GreyCat
Comment: stuff
Deletions are marked like this. Additions are marked like this.
Line 3: Line 3:
Well, that depends whether you want to store the command's ''output'' (either stdout, or stdout + stderr) or its ''exit status'' (0 to 255, with 0 typically meaning "success"). Well, that depends on whether you want to store the command's ''output'' (either stdout, or stdout + stderr) or its ''exit status'' (0 to 255, with 0 typically meaning "success").
Line 8: Line 8:
    var=$(command) # stdout only; stderr remains uncaptured
    var=$(command 2>&1) # both stdout and stderr will be captured
    output=$(command) # stdout only; stderr remains uncaptured
    output=$(command 2>&1) # both stdout and stderr will be captured
Line 12: Line 12:
If you want the exit status: If you want the exit status, you use the special parameter `$?` after running the command:
Line 16: Line 16:
    var=$?     status=$?
Line 22: Line 22:
    var1=$(command)
    var2=$?
    output=$(command)
    status=$?
Line 25: Line 25:
The assignment to {{{var1}}} has no effect on {{{command}}}'s exit status, which is still in {{{$?}}}. The assignment to {{{output}}} has no effect on {{{command}}}'s exit status, which is still in {{{$?}}}.
Line 27: Line 27:
If you don't ''actually'' want the exit status, but simply want to take an action upon success or failure: If you don't ''actually'' want to store the exit status, but simply want to take an action upon success or failure, just use `if`:
Line 30: Line 30:
    if command
   
then
    if command; then
Line 38: Line 37:
Or (shorter): Or if you want to capture stdout as well as taking action on success/failure, without explicitly storing or checking `$?`:
Line 41: Line 40:
    command && echo "it succeeded" || echo "it failed"     if output=$(command); then
       
echo "it succeeded"
...
Line 44: Line 45:
What if you want the exit status of a command in a few that are piped to each other? Use the {{{PIPESTATUS}}} array (BASH only). Say you want the exit status of {{{grep}}} in the following: What if you want the exit status of one command from a pipeline? If you want the last command's status, no problem -- it's in `$?` just like before. If you want some other command's status, use the {{{PIPESTATUS}}} array (BASH only). Say you want the exit status of {{{grep}}} in the following:
Line 48: Line 49:
    result=${PIPESTATUS[0]}     status=${PIPESTATUS[0]}
}}}

Bash 3.0 added a `pipefail` option as well, which can be used if you simply want to take action upon failure of the `grep`:

{{{
    set -o pipefail
    if ! grep foo somelogfile | head -5; then
        echo "uh oh"
    fi
Line 54: Line 64:
    var=$(command 2>&1 >/dev/null) # Save stderr, discard stdout.
    var=$(command 2>&1 >/dev/tty) # Save stderr, send stdout to the terminal.
    var=$(command 3>&2 2>&1 1>&3-) # Save stderr, send stdout to stderr
    output=$(command 2>&1 >/dev/null) # Save stderr, discard stdout.
    output=$(command 2>&1 >/dev/tty) # Save stderr, send stdout to the terminal.
    output=$(command 3>&2 2>&1 1>&3-) # Save stderr, send stdout to script's stderr.
Line 63: Line 73:
    var=$(command 2>&1 1>&3)    # Run command. stderr is captured.     output=$(command 2>&1 1>&3) # Run command. stderr is captured.
Line 66: Line 76:
    # Or this alternative:
    { var=$(command 2>&1 1>&3-) ;} 3>&1 # Capture stderr, let stdout through.
    # Or this alternative, which captures stderr, letting stdout through:
    { output=$(command 2>&1 1>&3-) ;} 3>&1
Line 70: Line 80:
In the last example above, note that {{{1>&3-}}} duplicates FD 3 and stores a copy in FD 1, and then closes FD 3. In the last example above, note that {{{1>&3-}}} duplicates FD 3 and stores a copy in FD 1, and then closes FD 3.  It could also be written `1>&3 3>&-`.
Line 72: Line 82:
What you ''cannot'' do is capture stdout in one variable, and stderr in another, using only FD redirections. You must use a temporary file to achieve that one. What you ''cannot'' do is capture stdout in one variable, and stderr in another, using only FD redirections. You must use a temporary file (or a named pipe) to achieve that one.
Line 82: Line 92:
''Note: the original question read, "How can I store the return value of a command in a variable?" This was, verbatim, an actual question asked in #bash.'' ''Note: the original question read, "How can I store the return value of a command in a variable?" This was, verbatim, an actual question asked in #bash, ambiguity and all.''

----
CategoryShell

How can I store the return value/output of a command in a variable?

Well, that depends on whether you want to store the command's output (either stdout, or stdout + stderr) or its exit status (0 to 255, with 0 typically meaning "success").

If you want to capture the output, you use command substitution:

    output=$(command)      # stdout only; stderr remains uncaptured
    output=$(command 2>&1) # both stdout and stderr will be captured

If you want the exit status, you use the special parameter $? after running the command:

    command
    status=$?

If you want both:

    output=$(command)
    status=$?

The assignment to output has no effect on command's exit status, which is still in $?.

If you don't actually want to store the exit status, but simply want to take an action upon success or failure, just use if:

    if command; then
        echo "it succeeded"
    else
        echo "it failed"
    fi

Or if you want to capture stdout as well as taking action on success/failure, without explicitly storing or checking $?:

    if output=$(command); then
        echo "it succeeded"
    ...

What if you want the exit status of one command from a pipeline? If you want the last command's status, no problem -- it's in $? just like before. If you want some other command's status, use the PIPESTATUS array (BASH only). Say you want the exit status of grep in the following:

    grep foo somelogfile | head -5
    status=${PIPESTATUS[0]}

Bash 3.0 added a pipefail option as well, which can be used if you simply want to take action upon failure of the grep:

    set -o pipefail
    if ! grep foo somelogfile | head -5; then
        echo "uh oh"
    fi

Now, some trickier stuff. Let's say you want only the stderr, but not stdout. Well, then first you have to decide where you do want stdout to go:

    output=$(command 2>&1 >/dev/null)  # Save stderr, discard stdout.
    output=$(command 2>&1 >/dev/tty)   # Save stderr, send stdout to the terminal.
    output=$(command 3>&2 2>&1 1>&3-)  # Save stderr, send stdout to script's stderr.

It's possible, although considerably harder, to let stdout "fall through" to wherever it would've gone if there hadn't been any redirection. This involves "saving" the current value of stdout, so that it can be used inside the command substitution:

    exec 3>&1                    # Save the place that stdout (1) points to.
    output=$(command 2>&1 1>&3)  # Run command.  stderr is captured.
    exec 3>&-                    # Close FD #3.

    # Or this alternative, which captures stderr, letting stdout through:
    { output=$(command 2>&1 1>&3-) ;} 3>&1

In the last example above, note that 1>&3- duplicates FD 3 and stores a copy in FD 1, and then closes FD 3. It could also be written 1>&3 3>&-.

What you cannot do is capture stdout in one variable, and stderr in another, using only FD redirections. You must use a temporary file (or a named pipe) to achieve that one.

Well, you can use a horrible hack like:

   result=$( { stdout=$(cmd) ; } 2>&1; echo "this line is the separator"; echo "$stdout")
   var_out=${result#*this line is the separator$'\n'}
   var_err=${result%$'\n'this line is the separator*}

Obviously, this is not robust, because either the standard output or the standard error of the command could contain whatever separator string you employ.

Note: the original question read, "How can I store the return value of a command in a variable?" This was, verbatim, an actual question asked in #bash, ambiguity and all.


CategoryShell

BashFAQ/002 (last edited 2023-05-11 13:36:34 by emanuele6)