A good way to grok the difference between them is to do a little experimenting on the command line. In spite of the visual similarity in use of the <
character, it does something very different than a redirect or pipe.
Let's use the date
command for testing.
$ date | cat
Thu Jul 21 12:39:18 EEST 2011
This is a pointless example but it shows that cat
accepted the output of date
on STDIN and spit it back out. The same results can be achieved by process substitution:
$ cat <(date)
Thu Jul 21 12:40:53 EEST 2011
However what just happened behind the scenes was different. Instead of being given a STDIN stream, cat
was actually passed the name of a file that it needed to go open and read. You can see this step by using echo
instead of cat
.
$ echo <(date)
/proc/self/fd/11
When cat received the file name, it read the file's content for us. On the other hand, echo just showed us the file's name that it was passed. This difference becomes more obvious if you add more substitutions:
$ cat <(date) <(date) <(date)
Thu Jul 21 12:44:45 EEST 2011
Thu Jul 21 12:44:45 EEST 2011
Thu Jul 21 12:44:45 EEST 2011
$ echo <(date) <(date) <(date)
/proc/self/fd/11 /proc/self/fd/12 /proc/self/fd/13
It is possible to combine process substitution (which generates a file) and input redirection (which connects a file to STDIN):
$ cat < <(date)
Thu Jul 21 12:46:22 EEST 2011
It looks pretty much the same but this time cat was passed STDIN stream instead of a file name. You can see this by trying it with echo:
$ echo < <(date)
<blank>
Since echo doesn't read STDIN and no argument was passed, we get nothing.
Pipes and input redirects shove content onto the STDIN stream. Process substitution runs the commands, saves their output to a special temporary file and then passes that file name in place of the command. Whatever command you are using treats it as a file name. Note that the file created is not a regular file but a named pipe that gets removed automatically once it is no longer needed.
Running the current shell under strace(1)
and then executing e.g. <(command)
gives:
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fa6713d59d0) = 13305
From a purely definitional standpoint, since clone(2) is defined as
create a child process
and a subshell as
Running a shell script launches a new process, a subshell.
one could say that yes - running process substitution is invoked as a subshell.
Best Answer
Process substitution is a feature that originated in the Korn shell in the 80s (in ksh86). At the time, it was only available on systems that had support for
/dev/fd/<n>
files.Later, the feature was added to
zsh
(from the start: 1990) andbash
(in 1993).zsh
was using temporary named pipes to implement it, whilebash
was using/dev/fd/<n>
where available and named pipes otherwise.zsh
switched to using/dev/fd/<n>
where available in2.6-beta17
in 1996.Support for process substitution via named pipes on systems without
/dev/fd
was only added toksh
inksh93u+
in 2012. The public domain clone ofksh
doesn't support it.To my knowledge, no other Bourne-like shell supports it (
rc
,es
,fish
, non-Bourne-like shells support it but with a different syntax).yash
has a<(...)
construct, but that's for process redirection.While quite useful, the feature was never standardized by POSIX. So, one can't expect to find it in
sh
, so shouldn't use it in ash
script.Though the behaviour for
<(...)
is unspecified in POSIX, (so there would be no harm in retaining it),bash
disables the feature when called assh
or when called withPOSIXLY_CORRECT=1
in its environment.So, if you have a script that uses
<(...)
, you should use a shell that supports the feature to interpret it likezsh
,bash
or AT&Tksh
(of course, you need to make sure the rest of the syntax of script is also compatible with that shell).In any case:
Can be written:
Or just
For a command other than
cat
(that needs to be passed data via a file given as argument), on systems with/dev/fd/x
, you can always do:Or if you need
that-cmd
's stdin to be preserved: