7. Command/Process Substitution — Zsh 5.9.999.3-test documentation

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

Example 7.1: Backtick and $(…) forms

    % 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`:

Example 7.2: TTY and =cmd shortcuts

    % 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:

Example 7.3: Setting NO_EQUALS

    % ls
    =foo    =bar
    % ls =foo =bar
    zsh: foo not found
    % setopt noequals
    % ls =foo =bar
    =foo    =bar

Another nice feature is process substitution:

Example 7.4: Process sub with =(…)

    % 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:

Example 7.5: print -l one per line

    % print -l foo bar
    foo
    bar

We could also have written:

Example 7.6: =(…) with multiline echo

    % who | fgrep -f =(echo 'root
    > lemke
    > shgchan
    > subbarao')

Using process substitution, you can edit the output of a command:

Example 7.7: Edit process sub output

    % 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:

Example 7.8: Read compressed mail folder

    % 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.

Example 7.9: diff two process outputs

    % 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.

Example 7.10: Output process sub >(…)

    % dvips -o >(lpr) zsh.dvi