Why is there a "/dev/fd/63" in the output of "echo 123 >(cat)"?
The process substitution >(thing)
will be replaced by a file name. This file name corresponds to a file that is connected to the standard input of the thing
inside the substitution.
The following would be a better example of its use:
$ sort -o >(cat -n >/tmp/out) ~/.profile
This would sort the file ~/.profile
and send the output to cat -n
which would enumerate the lines and store the result in /tmp/out
.
So, to answer you question: You get that output because echo
gets the two arguments 123
and /dev/fd/63
. /dev/fd/63
is the file connected to the standard input of the cat
process in the process substitution.
Modifying your example code slightly:
$ echo 101 > >(cat)
This would produce just 101
on standard output (the output of echo
would be redirected to the file that serves as the input to cat
, and cat
would produce the contents of that file on standard output).
Also note that in the cmd1 | cmd2
pipeline, cmd2
may not at all be running in the same shell as cmd1
(depending on the shell implementation you are using). ksh93
works the way you describe (same shell), while bash
creates a subshell for cmd2
(unless its lastpipe
shell option is set and job control is not active).
For completeness
cmd1 >(cmd2)
is mostly the same as
cmd1 | cmd2
in the yash
shell, and that shell only.
In that shell, >(cmd)
is process redirection as opposed to the >(cmd)
of ksh
/bash
/zsh
which is process substitution.
It's not strictly equivalent, because in cmd1 >(cmd2)
, yash
doesn't wait for cmd2
, so you may find that:
$ yash -c 'echo A >(cat); echo B'
B
A
$ yash -c 'echo A | cat; echo B'
A
B
By contrast, process substitution expands to a file path (typically a named pipe or a /dev/fd/<x>
where <x>
is a fd to a pipe that has been created beforehand) which, when open for writing will allow to send output to cmd
.
While process substitution was introduced by ksh
, in that shell, you can't pass them as argument to redirections.
ksh -c 'cmd1 > >(cmd2)'
to emulate yash
process redirection won't work. There, you're meant to pass that file name resulting from the substitution as argument to a command like in:
ksh -c 'diff <(echo a) <(echo b)'
It will work in bash
and zsh
.
However, in bash
like for yash
's process redirection, the shell doesn't wait for the command (cmd2
). So:
$ bash -c 'echo A > >(cat); echo B'
B
A
ksh
process substitution can be emulated with yash
with:
cmd1 /dev/fd/5 5>(cmd2)
Like:
diff /dev/fd/3 3<(echo a) /dev/fd/4 4<(echo b)
Because that's what process substitution does, it makes the command inside the substitution appear as a filename. Internally, it connects the commands via a pipe, giving the /dev/fd/NN
path to the main command, so it can open the already-open file descriptor to the pipe.
It's not the same as a pipe. Pipes connect stdout
to stdin
without involving anything that looks like file names. Process substitution is more flexible in that you can have multiple such substitutions in one command line, but it requires the main command to open files by name (e.g. cat
rather than echo
).
You can emulate a pipe with process substitution by doing something like this:
echo foo > >(cat -n)