> Hi newsgroup
> I need to execute 'command1 | command2' and get the exit status of
> command1. I did my googling first, and came up with this:
> ( ( (command1 >&3 2>&3 ; echo $? >&4) 3>&1 | \
> command2 >&3) 4>&1 | exit `cat`) 3>&1
> I must admit I am at a loss. What are fd's 3 and 4? Does the construct
>>&4 make 4 a duplicate of 1, or 1 a duplicate of 4? Can anyone explain
> the lines above please?
There is an explanation in the "CSH PROGRAMMING CONSIDERED HARMFUL" FAQ of
one way of doing it.
>&4 makes stdout a duplicate of file descriptor 4.
Things you need to know are that file descriptor manipulation is done left
to right, but that pipes are done before fd manipulation.
So the first thing that is done is fd 3 is set up as a duplicate of the
original stdout, and then a subshell is started to run
( (command1 >&3 2>&3 ; echo $? >&4) 3>&1 | \
command2 >&3) 4>&1 | exit `cat`
This sets up a pipe. The RHS of the pipe is easy, it is just
exit `cat`
so it reads from its standard input a value and then exits with it. The
LHS is now
( (command1 >&3 2>&3 ; echo $? >&4) 3>&1 | command2 >&3) 4>&1
with stdout (fd 1) going to the pipe. Remember that fd 3 is a duplicate of
the original stdout. So this sets up fd 4 as a duplicate of the pipe going
to exit `cat`, and runs another subshell.
(command1 >&3 2>&3 ; echo $? >&4) 3>&1 | command2 >&3
This sets up a pipe. The RHS is easy, it is just
command2 >&3
which runs command2 with its output going to fd 3, which is a duplicate of
the original stdout (fd 1). In other words this is making sure that the
output of command2 goes to the same place that it would have done if you
had just run
command1 | command2
The LHS is now
(command1 >&3 2>&3 ; echo $? >&4) 3>&1
with fd 4 going to the pipe to exit `cat`, and stdout (fd 1) going to
command2. It happens that fd 3 at this point is a duplicate of the
original stdout. Things are then overly complicated. It makes fd 3 a
duplicate of fd 1, the pipe to command2, and starts another subshell.
command1 >&3 2>&3 ; echo $? >&4
This runs command1, sending its standard output (fd 1) and standard error
(fd 2) to fd 3, the pipe to command2. It then echos the exit status of
command1 to fd 4, the pipe to exit `cat`.
So to answer your question "what is fd 4?" it is the pipe to the exit
command. "what is fd 3?" is either the original standard output of the
original, OR is a duplicate of the pipe to command2, depending on where it
is.
( ( (command1 2>&1 ; echo $? >&4) | command2 >&3) 4>&1 | exit `cat`) 3>&1
should do the same thing as
command1 2>&1 | command2
except for the exit status stuff. Normally one doesn't want to send stderr
from command1 to command2, unless command2 is something like 'more',
'less', 'pg' or 'lp'.
Quote:> There were other variations, but all involved temporary files which are
> not elegant. I myself came up with one of those:
> { { command1 ; echo $? > TMP_FILE ; } | command2 ; } ; EC=`cat TMP_FILE`
> ;\
> rm TMP_FILE ; exit $(EC)
> ... but that's less elegant, again. *** However, I was wondering whether
> there was ANY way (other than files) to communicate info from a subshell
> to the parent shell.
The other ways are via the exit status and file descriptors. Using
command substitution either backticks or $( ) is a special case of file
descriptors. If you are using 'rc' or a modern version of 'bash' there is
the '$status' (for rc) or '$PIPESTATUS' (for bash) variables which makes
your life much simpler. e.g.
#!/bin/bash
command1 | command2
exit ${PIPESTATUS[0]}
Quote:> I'm asking because command1 is executed in a subshell (a different
> process) because of the pipe. Whatever I set there (variables) won't be
> visible to the shell executing the pipe.
True
Quote:> But since the shell can pass variables (export) to it's subshells, it
> makes sense that they can return info in some manner. I could use that
> to carry out the exit status.
It may make sense to you, but that is not how Unix works! It is based on a
system call 'fork' which makes a copy of a process. The fact that it is a
copy, and eventually the copy calls 'exit' and is then thrown away means
that there is no backward channel for data to flow.