I have been working on this for some time, and it seems that there is no way to do this with a process replacement, except for using the built-in signaling, and this really can only be used for input channels, m will not expand on it.
However, bash -4.0 provides coprocesses that can be used to replace process substitution in this context and provide clean collection.
The following snippet provided by you:
git status --short | tee >(xargs -Istr test -z str)
could be replaced with something similar:
coproc GIT_XARGS { xargs -Istr test -z str; } { git status --short | tee; } >&${GIT_XARGS[1]} exec {GIT_XARGS[1]}>&- wait ${GIT_XARGS_PID}
Now, for some explanation:
Calling coproc
creates a new coprocess, calling it GIT_XARGS
(you can use any name) and run the command in braces. A pair of codes is created for the coprocess, redirecting it to stdin and stdout.
The coproc
call coproc
up two variables:
${GIT_XARGS[@]}
containing pipes for processing 'stdin and stdout, respectively ( [0]
for reading from stdout, [1]
for writing to stdin),${GIT_XARGS_PID}
containing the POP identifier for the coprocessor.
Then your command starts and its output goes to the second channel (i.e. coprocess' stdin). The mysterious looking part >&${GIT_XARGS[1]}
expanded to >&60
, which is a regular redirection of output to fd.
Note that I needed to put your command in braces. This is because the pipeline causes subprocesses to spawn, and they do not inherit file descriptors from the parent process. In other words, the following:
git status --short | tee >&${GIT_XARGS[1]}
will fail with an invalid file descriptor, since the corresponding fd exists in the parent process, and not in the spawned tee
process. Bracketing calls bash to apply the redirection to the entire pipeline.
The exec
call is used to close the channel for your coprocess. When you used process substitution, the process was spawned as part of the output redirection, and the pipe to it was closed immediately after the redirect, which no longer had an effect. Since the coprocessor trunk life is beyond the scope of a single redirection, we must explicitly close it.
Closing the outlet pipe should result in the process receiving an EOF condition for stdin and preserving it. We use wait
to wait for it to complete and get it. wait
returns the completion status of the process.
As a final note, note that in this case you cannot use kill
to terminate the code, as this will change its exit status.