I'm trying to understand how groups of sequential commands (named "lists" in GNU bash manual) work with pipes in POSIX sh definition or its implementations. For example, the following code:
if test-expr1; then
cmd1; cmd2; cmd3; ...
fi | { cmd4; cmd5; ...; } | cmd6
Here, the pipeline has two sequential "lists" and a single command, where cmd2 executes after cmd1 finishes and cmd5 after cmd4. However, all three are run in parallel so the pipeline doesn't block.
My first guess was that for each of these "lists", a subshell is created, with a separate PID, which executes all commands in sequence, but doesn't block the shell from spawning other processes — which would happen if shell started waiting for cmd1 to complete. This was incorrect, though, as echo $$ still reports the same PID even in piped command lists.
The question is: how exactly are fork()/waitpid() used to achieve the correct behavior?