7. Command/Process Substitution¶
Command substitution in zsh can take two forms. In the traditional form, a command enclosed in backquotes (`…`) is replaced on the command line with its output. This is the form used by the older shells. Newer shells (like zsh) also provide another form, $(…). This form is much easier to nest.
% ls -l `echo /vmunix`
-rwxr-xr-x 1 root 1209702 May 14 19:04 /vmunix
% ls -l $(echo /vmunix)
-rwxr-xr-x 1 root 1209702 May 14 19:04 /vmunix
% who | grep mad
subbarao ttyt7 May 23 15:02 (mad55sx15.Prince)
pfalstad ttyu1 May 23 16:25 (mad55sx14.Prince)
subbarao ttyu6 May 23 15:04 (mad55sx15.Prince)
pfalstad ttyv3 May 23 16:25 (mad55sx14.Prince)
% who | grep mad | awk '{print $2}'
ttyt7
ttyu1
ttyu6
ttyv3
% cd /dev; ls -l $(who |
> grep $(echo mad) |
> awk '{ print $2 }')
crwx-w---- 1 subbarao 20, 71 May 23 18:35 ttyt7
crw--w---- 1 pfalstad 20, 81 May 23 18:42 ttyu1
crwx-w---- 1 subbarao 20, 86 May 23 18:38 ttyu6
crw--w---- 1 pfalstad 20, 99 May 23 18:41 ttyv3
Many common uses of command substitution, however, are superseded by other mechanisms of zsh:
% ls -l `tty`
crw-rw-rw- 1 root 20, 28 May 23 18:35 /dev/ttyqc
% ls -l $TTY
crw-rw-rw- 1 root 20, 28 May 23 18:35 /dev/ttyqc
% ls -l `which rn`
-rwxr-xr-x 1 root 172032 Mar 6 18:40 /usr/princeton/bin/rn
% ls -l =rn
-rwxr-xr-x 1 root 172032 Mar 6 18:40 /usr/princeton/bin/rn
A command name with a = prepended is replaced with its full pathname. This can be very convenient. If it’s not convenient for you, you can turn it off:
% ls
=foo =bar
% ls =foo =bar
zsh: foo not found
% setopt noequals
% ls =foo =bar
=foo =bar
Another nice feature is process substitution:
% who | fgrep -f =(print -l root lemke shgchan subbarao)
root console May 19 10:41
lemke ttyq0 May 22 10:05 (narnia:0.0)
lemke ttyr7 May 22 10:05 (narnia:0.0)
lemke ttyrd May 22 10:05 (narnia:0.0)
shgchan ttys1 May 23 16:52 (gaudi.Princeton.)
subbarao ttyt7 May 23 15:02 (mad55sx15.Prince)
subbarao ttyu6 May 23 15:04 (mad55sx15.Prince)
shgchan ttyvb May 23 16:51 (gaudi.Princeton.)
A command of the form =(…) is replaced with the name of a file containing its output. (A command substitution, on the other hand, is replaced with the output itself.) print -l is like echo, excepts that it prints its arguments one per line, the way fgrep expects them:
% print -l foo bar
foo
bar
We could also have written:
% who | fgrep -f =(echo 'root
> lemke
> shgchan
> subbarao')
Using process substitution, you can edit the output of a command:
% ed =(who | fgrep -f ~/.friends)
355
g/lemke/d
w /tmp/filbar
226
q
% cat /tmp/filbar
root console May 19 10:41
shgchan ttys1 May 23 16:52 (gaudi.Princeton.)
subbarao ttyt7 May 23 15:02 (mad55sx15.Prince)
subbarao ttyu6 May 23 15:04 (mad55sx15.Prince)
shgchan ttyvb May 23 16:51 (gaudi.Princeton.)
or easily read archived mail:
% mail -f =(zcat ~/mail/oldzshmail.Z)
"/tmp/zsha06024": 84 messages, 0 new, 43 unread
> 1 U TO: pfalstad, zsh (10)
2 U nytim!tim@uunet.uu.net, Re: Zsh on Sparc1 /SunOS 4.0.3
3 U JAM%TPN@utrcgw.utc.com, zsh fix (15)
4 U djm@eng.umd.edu, way to find out if running zsh? (25)
5 U djm@eng.umd.edu, Re: way to find out if running zsh? (17)
6 r djm@eng.umd.edu, Meta . (18)
7 U jack@cs.glasgow.ac.uk, Re: problem building zsh (147)
8 U nytim!tim@uunet.uu.net, Re: Zsh on Sparc1 /SunOS 4.0.3
9 ursa!jmd, Another fix... (61)
10 U pplacewa@bbn.com, Re: v18i084: Zsh 2.00 - A small complaint (36)
11 U lubkin@cs.rochester.edu, POSIX job control (34)
12 U yale!bronson!tan@uunet.UU.NET
13 U brett@rpi.edu, zsh (36)
14 S subbarao, zsh sucks!!!! (286)
15 U snibru!d241s008!d241s013!ala@relay.EU.net, zsh (165)
16 U nytim!tim@uunet.UU.NET, Re: Zsh on Sparc1 /SunOS 4.0.3
17 U subbarao, zsh is a junk shell (43)
18 U amaranth@vela.acs.oakland.edu, zsh (33)
43u/84 1: x
% ls -l /tmp/zsha06024
/tmp/zsha06024 not found
Note that the shell creates a temporary file, and deletes it when the command is finished.
% diff =(ls) =(ls -F)
3c3
< fortune
---
> fortune*
10c10
< strfile
---
> strfile*
If you read zsh’s man page, you may notice that <(…) is another form of process substitution which is similar to =(…). There is an important difference between the two. In the <(…) case, the shell creates a named pipe (FIFO) instead of a file. This is better, since it does not fill up the file system; but it does not work in all cases. In fact, if we had replaced =(…) with <(…) in the examples above, all of them would have stopped working except for fgrep -f <(…). You can not edit a pipe, or open it as a mail folder; fgrep, however, has no problem with reading a list of words from a pipe. You may wonder why diff <(foo) bar doesn’t work, since foo | diff - bar works; this is because diff creates a temporary file if it notices that one of its arguments is -, and then copies its standard input to the temporary file.
>(…) is just like <(…) except that the command between the parentheses will get its input from the named pipe.
% dvips -o >(lpr) zsh.dvi