[ prev | next | top ]

## 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

[ prev | next | top ]