User Tools

Site Tools


bash:output:get_the_output_of_a_command

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
bash:output:get_the_output_of_a_command [2021/01/26 15:03] peterbash:output:get_the_output_of_a_command [2021/01/26 15:07] (current) peter
Line 79: Line 79:
 var_err=${result%this is the separator*} var_err=${result%this is the separator*}
 </code> </code>
 +
 +----
 +
 +===== Only keep stderr =====
 +
 +Say you want only the stderr, but not stdout.
 +
 +Then first you have to decide where you do want stdout to go:
 +
 +<code bash>
 +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.
 +</code>
 +
 +Since the last example may seem a bit confusing, here is the explanation.
 +
 +  * First, keep in mind that **1>&3-** is equivalent to **1>&3 3>&-**.
 +  * So it will be easier to analyze the following sequence: $(... 3>&2 2>&1 1>&3 3>&-)
 +
 +^Redirection^fd 0 (stdin)^fd 1 (stdout)^fd 2 (stderr)^fd 3^Description^
 +|initial|/dev/tty|/dev/tty|/dev/tty| |Let's assume this is run in a terminal, so stdin, stdout and stderr are all initially connected to the terminal (tty).|
 +|$(...)|/dev/tty|**pipe**|/dev/tty| |First, the command substitution is set up. Command's stdout (FileDescriptor 1) gets captured (by using a pipe internally). Command's stderr (FD 2) still points to its regular place (the script's stderr).|
 +|3>&2|/dev/tty|pipe|/dev/tty|**/dev/tty**|Next, FD 3 should point to what FD 2 points to at this very moment, meaning FD 3 will point to the script's stderr ("save stderr in FD 3").|
 +|2>&1|/dev/tty|pipe|**pipe**|/dev/tty|Next, FD 2 should point to what FD 1 currently points to, meaning FD 2 will point to stdout. Right now, both FD 2 and FD 1 would be captured.|
 +|1>&3|/dev/tty|**/dev/tty**|pipe|/dev/tty|Next, FD 1 should point to what FD 3 currently points to, meaning FD 1 will point to the script's stderr. FD 1 is no longer captured. We have "swapped" FD 1 and FD 2.|
 +|3>&-|/dev/tty|/dev/tty|pipe| |Finally, we close FD 3 as it is no longer necessary.|
 +
 +A little note: operation **n>&m-** is sometimes called moving FD m to FD n.
 +
 +This way what the script writes to FD 2 (normally stderr) will be written to stdout because of the second redirection.  What the script writes to FD 1 (normally stdout) will be written to stderr because of the first and third redirections.  Stdout and stderr got replaced.  Done.
 +
 +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:
 +
 +<code bash>
 +exec 3>&                   # 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
 +</code>
 +
 +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>&-.
 +
 +----
 +
  
bash/output/get_the_output_of_a_command.1611673429.txt.gz · Last modified: 2021/01/26 15:03 by peter

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki