This is an old revision of the document!

Process substitution

<( <LIST> )

>( <LIST> )

This only works on systems that support named pipes (FIFO - a special file) or the /dev/fd/NNNNN-syntax to access open files.

Process substitution is performed simultaneously with parameter expansion, command substitution and arithmetic expansion.

The command list <LIST> is executed and its

  • standard output filedescriptor in the <( … ) form or
  • standard input filedescriptor in the >( … ) form

is connected to a FIFO or a file in /dev/fd/. The filename (where the filedescriptor is connected) is then used as a substitution for the <(…)-construct.

That, for example, allows to give data to a command that can't be reached by pipelining (that doesn't expect its data from stdin but from a file).

If a process substitution is expanded as an argument to a function, expanded to an environment variable during calling of a function, or expanded to any assignment within a function, the process substitution will be "held open" for use by any command within the function or its callees, until the function in which it was set returns. If the same variable is set again within a callee, unless the new variable is local, the previous process substitution is closed and will be unavailable to the caller when the callee returns.

In essence, process substitutions expanded to variables within functions remain open until the function in which the process substitution occured returns - even when assigned to locals that were set by a function's caller. Dynamic scope doesn't protect them from closing.

This code is useless, but it demonstrates how it works:

$ echo <(ls)

The output of the ls-program can be accessed by reading the file /dev/fd/63.

This is very useful in redirection statements. A wrong piece of code to count all files in /etc is:

find /etc | while read; do
echo "$counter files"

Why this is wrong? Due to the pipe, the while read; do … done part is executed in a subshell. That also means counter is incremented in that subshell. When the pipeline finishes, the subshell is terminated, and the counter you access is the counter from the main shell - the one at "0"!

Process substitution helps us here to avoid the pipe (which is the reason for the subshell):

while read; do
done < <(find /etc)
echo "$counter files"

This is the normal input file redirection < FILE, just that FILE is dynamically generated by process substitution:


This example demonstrates how process substitutions can be made to resemble "passable" objects. This results in converting the output of f's argument to uppercase.

f() {
    cat "$1" >"$x"
x=>(tr '[:lower:]' '[:upper:]') f <(echo 'hi there')

  • Process substitution is not specified by POSIX.
  • Process substitution is disabled completely in Bash POSIX mode.
  • Process substitution is implemented by Bash, Zsh, Ksh{88,93}, but not (yet) pdksh derivatives (mksh). Coprocesses may be used instead.
  • Bash evaluates process substitutions within array indices, but not other arithmetic contexts. Ksh and Zsh do not. (Possible Bug)

  	# print "moo"
	dev=fd=1 _[1<(echo moo >&2)]=
	# fork bomb

This website uses cookies for visitor traffic analysis. By using the website, you agree with storing the cookies on your computer.More information
liungkejin, 2013/05/31 11:46, 2013/06/22 11:58

I found a fun thing:

(echo "YES")> >(read str; echo "1:${str}:first";)> >(read sstr; echo "2:$sstr:two")> >(read ssstr; echo "3:$ssstr:three")

You could leave a comment if you were logged in.
  • syntax/expansion/proc_subst.1358188340.txt
  • Last modified: 2013/01/14 18:32
  • by ormaaj