I'd like to redirect the stdout of process proc1 to two processes proc2 and proc3:
proc2 -> stdout
/
proc1
\
proc3 -> stdout
I tried
proc1 | (proc2 & proc3)
but it doesn't seem to work, i.e.
echo 123 | (tr 1 a & tr 1 b)
writes
b23
to stdout instead of
a23
b23
Since @dF: mentioned that PowerShell has tee, I thought I'd show a way to do this in PowerShell.
Note that each object coming out of the first command is processed before the next object is created. This can allow scaling to very large inputs.
Unix (
bash
,ksh
,zsh
)dF.'s answer contains the seed of an answer based on
tee
and output process substitutions(
>(...)
) that may or may not work, depending on your requirements:Note that process substitutions are a nonstandard feature that (mostly) POSIX-features-only shells such as
dash
(which acts as/bin/sh
on Ubuntu, for instance), do not support. Shell scripts targeting/bin/sh
should not rely on them.The pitfalls of this approach are:
unpredictable, asynchronous output behavior: the output streams from the commands inside the output process substitutions
>(...)
interleave in unpredictable ways.In
bash
andksh
(as opposed tozsh
- but see exception below):bash
andksh
do not wait for the output process substitution-spawned processes to finish, at least by default.zsh
is the only shell that does by default wait for the processes run in the output process substitutions to finish, except if it is stderr that is redirected to one (2> >(...)
).ksh
(at least as of version93u+
) allows use of argument-lesswait
to wait for the output process substitution-spawned processes to finish.Note that in an interactive session that could result in waiting for any pending background jobs too, however.
bash v4.4+
can wait for the most recently launched output process substitution withwait $!
, but argument-lesswait
does not work, making this unsuitable for a command with multiple output process substitutions.However,
bash
andksh
can be forced to wait by piping the command to| cat
, but note that this makes the command run in a subshell. Caveats:ksh
(as ofksh 93u+
) doesn't support sending stderr to an output process substitution (2> >(...)
); such an attempt is silently ignored.While
zsh
is (commendably) synchronous by default with the (far more common) stdout output process substitutions, even the| cat
technique cannot make them synchronous with stderr output process substitutions (2> >(...)
).However, even if you ensure synchronous execution, the problem of unpredictably interleaved output remains.
The following command, when run in
bash
orksh
, illustrates the problematic behaviors (you may have to run it several times to see both symptoms): TheAFTER
will typically print before output from the output substitutions, and the output from the latter can be interleaved unpredictably.In short:
Guaranteeing a particular per-command output sequence:
bash
norksh
norzsh
support that.Synchronous execution:
zsh
, they're invariably asynchronous.ksh
, they don't work at all.If you can live with these limitations, using output process substitutions is a viable option (e.g., if all of them write to separate output files).
Note that tzot's much more cumbersome, but potentially POSIX-compliant solution also exhibits unpredictable output behavior; however, by using
wait
you can ensure that subsequent commands do not start executing until all background processes have finished.See bottom for a more robust, synchronous, serialized-output implementation.
The only straightforward
bash
solution with predictable output behavior is the following, which, however, is prohibitively slow with large input sets, because shell loops are inherently slow.Also note that this alternates the output lines from the target commands.
Unix (using GNU Parallel)
Installing GNU
parallel
enables a robust solution with serialized (per-command) output that additionally allows parallel execution:parallel
by default ensures that output from the different commands doesn't interleave (this behavior can be modified - seeman parallel
).Note: Some Linux distros come with a different
parallel
utility, which won't work with the command above; useparallel --version
to determine which one, if any, you have.Windows
Jay Bazuzi's helpful answer shows how to do it in PowerShell. That said: his answer is the analog of the looping
bash
answer above, it will be prohibitively slow with large input sets and also alternates the output lines from the target commands.bash
-based, but otherwise portable Unix solution with synchronous execution and output serializationThe following is a simple, but reasonably robust implementation of the approach presented in tzot's answer that additionally provides:
While not strictly POSIX compliant, because it is a
bash
script, it should be portable to any Unix platform that hasbash
.Note: You can find a more full-fledged implementation released under the MIT license in this Gist.
If you save the code below as script
fanout
, make it executable and put int yourPATH
, the command from the question would work as follows:fanout
script source code:another way to do would be,
output:
no need to create a subshell here
Editor's note:
-
>(…)
is a process substitution that is a nonstandard shell feature of some POSIX-compatible shells:bash
,ksh
,zsh
.- This answer accidentally sends the output process substitution's output through the pipeline too:
echo 123 | tee >(tr 1 a) | tr 1 b
.- Output from the process substitutions will be unpredictably interleaved, and, except in
zsh
, the pipeline may terminate before the commands inside>(…)
do.In unix (or on a mac), use the
tee
command:Usually you would use
tee
to redirect output to multiple files, but using >(...) you can redirect to another process. So, in general,will do what you want.
Under windows, I don't think the built-in shell has an equivalent. Microsoft's Windows PowerShell has a
tee
command though.Like dF said,
bash
allows to use the>(…)
construct running a command in place of a filename. (There is also the<(…)
construct to substitute the output of another command in place of a filename, but that is irrelevant now, I mention it just for completeness).If you don't have bash, or running on a system with an older version of bash, you can do manually what bash does, by making use of FIFO files.
The generic way to achieve what you want, is:
NOTE: for compatibility reasons, I would do the
$(…)
with backquotes, but I couldn't do it writing this answer (the backquote is used in SO). Normally, the$(…)
is old enough to work even in old versions of ksh, but if it doesn't, enclose the…
part in backquotes.