Capture stdout to a variable but still display it in the console
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
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
Here's an example capturing both stderr
and the command's exit code. This is building on the answer by Russell Davis.
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.
If you omit 2>&1
then only stdout
will be captured.
If by "the console" you mean your current TTY, try
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).
You can use more than three file descriptors. Try here:
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."
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.