I often want to perform an operation that requires
- splitting the
stdout
of a pipeline (let's call itpipeline-before
) into two parallel streams; - feeding the resulting stream, as their
stdin
, to two separate pipelines (pipeline-between-0
andpipeline-between-1
); - merging the two resulting
stdout
streams in a strict sequence; - feeding the resulting merged stream, as its
stdin
to another pipeline (pipeline-after
).
In (3), by "in a strict sequence" I mean that all the output coming from, say, pipeline-between-0
should appear in the merged output stream before any of the output coming from pipeline-between-1
.
The whole thing could be diagrammed like this:
pipeline-before --.--- pipeline-between-0 -.
\ \
`- pipeline-between-1 ---`-- pipeline-after
An example of such pipeline-between-0
/pipeline-between-1
pair could be:
head -n 1 | tr 'a-z' 'A-Z'
tail -n +2 | sort -t $'\t' -k1,1
In English, one would describe this combination as "make the first row all uppercase, and sort the remaining rows by the first column."
Q: Is there a general shell syntax to express such an operation?
I am interested in the answer to this question for both zsh
and bash
.
Here's one syntax that, in general, does not work:
$ pipeline-before | tee >( pipeline-between-0 ) | pipeline-between-1 | pipeline-after
This syntax fails for two reasons:
- some of the output of
pipeline-between-1
often appears before some of the output ofpipeline-between-0
. - the final output is truncated (I suspect that the reason for this is a
SIGPIPE
signal).
I did try the following maneuver (which, I confess, I don't understand 100%):
{
pipeline-before |
{ tee >( pipeline-between-0 4>&1 1>&3 ); } |
pipeline-between-1
} 3>&1 | pipeline-after
AFAICT, this syntax seems to solve the first of the problems listed above (i.e. based on a few informal tests, the outputs of pipeline-between-0
and pipeline-between-1
show up in the correct sequence). Unfortunately, though, the final output is still truncated, at least in some cases.