Bash – Understanding Output Redirection

file-descriptorsio-redirectionpipeshell

This script was posted as answer to a Question.
And I'm trying to work out what's going on.

result=$(
  {
    {
      ssh host app-status >&3 3>&-; echo "$?"
    } | {
      until read -t1 ret; do
        printf . >&2
      done
      exit "$ret"
    }
  } 3>&1
)

Here's my explanation, but I'm sure it's wrong.

  1. The outer {...} 3>&1 : fd 3 is opened as a duplicate of stdout
  2. Then second half of pipe, '| { }` no redirection
  3. In read loop printf's stdout is duplicated with stderr, so in 2. (above) all output actually comes via stderr.
  4. Now the first half of pipe, { ssh ... } | : stdout of ssh is dupped onto fd3 (which is in fact stdout from 1. fd 3 is closed with 3>&- so stdout is re-opened onto what it was originally and this is piped into 2.
  5. finally, echo just prints to stdout

So my question (problem with understanding) is; Is the result of this the same as just printf to stderr ? What does the voodo redirect to 3 actually achieve?
Is there anything asynchronous in here ?

Best Answer

Inside $(...), stdout (fd 1) is a pipe. At the other end of the pipe, the shell reads the output and stores it into the $result variable.

With: $({ blah; } 3>&1) we make it that both fd 3 and 1 point to that pipe in blah.

blah is cmd1 | cmd2. There cmd1 and cmd2 are started concurrently with cmd1 fd 1 pointing to another pipe (the one to cmd2), however we don't want ssh output to go to that pipe, we want it to go to the first pipe so that it can be stored in $result.

So we have { ssh >&3; echo "$?"; } | cmd2, so that only the echo output goes to the pipe to cmd2. ssh output (fd 1) goes to $result. ssh not needing a fd 3, we close it for it after we've used it to set fd 1 (3>&-).

cmd2's input (fd 0) is the second pipe. Nothing is writing to that pipe (since ssh is writing to the first pipe) until ssh terminates and echo outputs the exit status there.

So in cmd2 (being the until loop), the read -t1 is actually waiting with timeout until ssh exits. After which, read will return successfully with the content of $? fed by echo.

Related Question