This is a nit, but where Hans referred to "pipe commands", I would use "piped commands".  To me, the pipe "commands" would be the '|' symbols themselves for invoking the pipes.  Not important, just my grammatical foible.

Hans gave this example:

( ls /tmp/afjkasdlf | grep georg | echo; pipestat=( ${PIPESTATUS[@]} );
echo "<${pipestat[0]}>"; echo "<${pipestat[1]}>" )

ls: cannot access /tmp/afjkasdlf: No such file or directory
<2>
<1>

At first I wondered why the pipe to echo.  I now understand it accomplishes two things:
- prevents grep from displaying results by piping stdout to a command that ignores stdin
- it outputs a blank line before the result of the commands in the pipe is displayed
Though I am not sure why the blank line comes out before the error message from ls. Probably has something to do with the way pipes function in relation to stdout.

I also was confused because he ignored the piped command "echo" in his explanation and it made me curious about more than just verifying what would happen if each of the piped commands was made to fail or succeed by changing the arguments.  I made a script called test based on derHans' example. 

This made it easy to make mods in one window, save them and execute in a terminal.  Pretty basic stuff.  After running a number of tests, I realized it was not possible to see which version of test was used for each run n the terminal.  So I added a cat test to the beginning of the script.  This directory contains among other things the test script and some java error logs named hs_err_pidnnnn.log.  The script was to show the return values for all three piped commands plus a 4th just to see what would come out.

I saw some things that were hard to understand. 

larry@triggerfish:~$ ./test
cat test

( ls -l hs* | grep test | echo; pipestat=( ${PIPESTATUS[@]} );
echo "ls rv=<${pipestat[0]}>"; echo "grep rv=<${pipestat[1]}>"; echo "echo rv=<${pipestat[1]}>"; echo "4th rv=<${pipestat[1]}>" )

ls rv=<0>
grep rv=<1>
echo rv=<1>
4th rv=<1>
This came out as expected. ls found the error logs but grep found no occurrences of "test"
 
larry@triggerfish:~$ ./test
cat test

( ls -l hs* | grep log | echo; pipestat=( ${PIPESTATUS[@]} );
echo "ls rv=<${pipestat[0]}>"; echo "grep rv=<${pipestat[1]}>"; echo "echo rv=<${pipestat[1]}>"; echo "4th rv=<${pipestat[1]}>" )

ls rv=<0>
grep rv=<141>
echo rv=<141>
4th rv=<141>
larry@triggerfish:~$ man grep
This was a complete surprise, grep should have succedded with multiple occurrences of "log" and nothing in the man page for grep suggested a meaning for the 141. Maybe it was the multiple files found, or multiple finds by grep, or something about open files, or ...  So I tried a few experiments:
 
larry@triggerfish:~$ ./test
cat test; sync

( ls -l test | grep test | echo; pipestat=( ${PIPESTATUS[@]} );
echo "ls rv=<${pipestat[0]}>"; echo "grep rv=<${pipestat[1]}>"; echo "echo rv=<${pipestat[1]}>"; echo "4th rv=<${pipestat[1]}>" )

ls rv=<0>
grep rv=<0>
echo rv=<0>
4th rv=<0>
So back to single files found and single grep results and with syncing (not sure why I tried that) worked.
 
larry@triggerfish:~$ ./test
cat test;

( ls -l test | grep test | echo; pipestat=( ${PIPESTATUS[@]} );
echo "ls rv=<${pipestat[0]}>"; echo "grep rv=<${pipestat[1]}>"; echo "echo rv=<${pipestat[1]}>"; echo "4th rv=<${pipestat[1]}>" )

ls rv=<0>
grep rv=<0>
echo rv=<0>
4th rv=<0>
So did removing the sync. but leaving the semicolon.
 
larry@triggerfish:~$ ./test
cat test

( ls -l test | grep test | echo; pipestat=( ${PIPESTATUS[@]} );
echo "ls rv=<${pipestat[0]}>"; echo "grep rv=<${pipestat[1]}>"; echo "echo rv=<${pipestat[1]}>"; echo "4th rv=<${pipestat[1]}>" )

ls rv=<0>
grep rv=<141>
echo rv=<141>
4th rv=<141>
Aah, but dropping the semicolon caused the 141's and on a case that should be all successes.

larry@triggerfish:~$ ./test
cat test ;

( ls -l test | grep test | echo; pipestat=( ${PIPESTATUS[@]} );
echo "ls rv=<${pipestat[0]}>"; echo "grep rv=<${pipestat[1]}>"; echo "echo rv=<${pipestat[1]}>"; echo "4th rv=<${pipestat[1]}>" )

ls rv=<0>
grep rv=<141>
echo rv=<141>
4th rv=<141>
But so did putting it back but with whitespace before it.  Huh?
 
larry@triggerfish:~$ ./test
cat test;

( ls -l test | grep test | echo; pipestat=( ${PIPESTATUS[@]} );
echo "ls rv=<${pipestat[0]}>"; echo "grep rv=<${pipestat[1]}>"; echo "echo rv=<${pipestat[1]}>"; echo "4th rv=<${pipestat[1]}>" )

ls rv=<0>
grep rv=<141>
echo rv=<141>
4th rv=<141>
Ditto whith no white space.
 
larry@triggerfish:~$ ./test
cat test; sync

( ls -l test | grep test | echo; pipestat=( ${PIPESTATUS[@]} );
echo "ls rv=<${pipestat[0]}>"; echo "grep rv=<${pipestat[1]}>"; echo "echo rv=<${pipestat[1]}>"; echo "4th rv=<${pipestat[1]}>" )

ls rv=<0>
grep rv=<141>
echo rv=<141>
4th rv=<141>
and with a sync but no trailing semicolon
 
larry@triggerfish:~$ ./test
cat test; sync;

( ls -l test | grep test | echo; pipestat=( ${PIPESTATUS[@]} );
echo "ls rv=<${pipestat[0]}>"; echo "grep rv=<${pipestat[1]}>"; echo "echo rv=<${pipestat[1]}>"; echo "4th rv=<${pipestat[1]}>" )

ls rv=<0>
grep rv=<0>
echo rv=<0>
4th rv=<0>
larry@triggerfish:~$
But adding a trailing semicolon fixed it again.

So it seems to me that there must be more than one behaviour involved.  It as if the contents of PIPESTATUS and therefore of pipestat are affected by things that happened and completed before the execution of the piped commands.  Very puzzling to me!  Am I missing something basic?

--
Dazed_75 a.k.a. Larry

The spirit of resistance to government is so valuable on certain occasions, that I wish it always to be kept alive.
  - Thomas Jefferson