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.
- The outer
{...} 3>&1
: fd 3 is opened as a duplicate of stdout - Then second half of pipe, '| { }` no redirection
- In read loop printf's stdout is duplicated with stderr, so in 2. (above) all output actually comes via stderr.
- 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 with3>&-
so stdout is re-opened onto what it was originally and this is piped into 2. - 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 ?
3 Answers 3
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
.
-
Argh! I see, ssh does go to original stdput and produces result which is what you want, and nothing is sent through pipe by ssh, only the
echo $?
of ssh. :-) very clever ! I like the read loop timeout too. tksX Tian– X Tian2014年02月06日 12:56:01 +00:00Commented Feb 6, 2014 at 12:56
Ok, firstly what happens if you execute a remote command via ssh
ssh otherhost /bin/true; echo $?
0
and
ssh otherhost /bin/false; echo $?
1
so echo "$?"
prints the return value of app-status
to stdout (file handle 1)
- The outer
{...} 3>&1
redirects all input from file handle 3 to stdout >&3 3>&-
redirects stdout to file handle 3 and closes file handle 3.read -t1 ret
reads input from stdin to variableret
printf . >&2
prints.
to stderr (file handle 2)
Good read Advanced Bash Scripting Guide
The basic idea behind the voodo redirections of fd 3 to fd 1 and fd 1 to fd 3 in the group command { ... 1>&3 3>&- ...} 3>&- 3>&1
is to make the output of the ssh
command bypass the ssh | until
pipe via fd 3.
Anything written to fd 3 inside the group command with 1>&3
will get around the pipe mechanism.
This way the read
command (which will "return failure if a complete line of input is not read within TIMEOUT seconds", see help read
in bash) will only get to read the exit status of the ssh
command and the output assigned to the result
variable can be restricted to the output of the ssh
command.
# examples for bypassing a pipe using output redirections
# cf. http://unix.stackexchange.com/a/18711
echo hello | sed 's/hello/HELLO/'
{ echo hello 1>&3 3>&- | sed 's/hello/HELLO/'; } 3>&- 3>&1
{
result=$(
{
{
ssh localhost 'sleep 5; echo "ssh output"; exit 55' >&3 3>&-; echo "$?"
} | {
until read -t1 ret; do
printf . >&2
done
printf '\n%s\n' "exit $ret" >&2
exit "$ret"
}
} 3>&1
)
echo "result: $result"
}
# output:
# .....
# exit 55
# result: ssh output
And, last but not least, duplicating a stdout stream is done by man 1 tee
, not by output redirections.
The outer {...} 3>&1
redirection means that fd 3 is opened and points to where fd 1 is pointing to, i. e. to stdout.
Further reading:
You must log in to answer this question.
Explore related questions
See similar questions with these tags.
exit: : numeric argument required
orread: read error: 0: Permission denied
followed by an infinite series of dots, depending on how app-status behaves on the remote side.