## 28.14. Command Substitution

A pair of backquotes (``) does command substitution. This is really useful -- it lets you use the standard output from one command as arguments to another command.

Here's an example. Assume you want to edit all files in the current directory that contain the word "error." Type this:

`-l` Section 33.6

>     $ vi `grep -l error *.c`
>     3 files to edit
>     "bar.c" 254 lines, 28338 characters
>        ...
>     $

But why does this work? How did we build the incantation above? First, think about how you'd do this without using any special techniques. You'd use grep to find out which commands contain the word "error"; then you'd use vi to edit this list:

>     $ grep error *.c
>     bar.c:  error("input too long");
>     bar.c:  error("input too long");
>     baz.c:  error("data formatted incorrectly");
>     foo.c:  error("can't divide by zero"):
>     foo.c:  error("insufficient memory"):
>     $ vi bar.c baz.c foo.c

Is there any way to compress these into one command? Yes, by using command substitution. First, we need to modify our grep command so that it produces only a list of filenames, rather than filenames and text. That's easy; use grep -l:

>     $ grep -l error *.c
>     bar.c
>     baz.c
>     foo.c

The -l option lists each filename only once, even if many lines in the file match. (This makes me think that grep -l was designed with precisely this application in mind.) Now, we want to edit these files; so we put the grep command inside backquotes, and use it as the argument to vi:

>     $ vi `grep -l error *.c`
>     3 files to edit
>     "bar.c" 254 lines, 28338 characters
>        ...
>     $

You might be wondering about the difference between the "vertical" output from grep and the "horizontal" way that people usually type arguments on a command line. The shell handles this with no problems. Inside backquotes, both a newline and a space are argument separators.

The list you use with command substitution doesn't have to be filenames. Let's see how to send a mail message (Section 1.21) to all the users logged on to the system now. You want a command line like this:

>     % mail joe lisa franka mondo bozo harpo ...

Getting there takes a little thinking about what Unix commands you need to run to get the output you want. (This is real "Power Tools" stuff!) To get a list of those users, you could use who (Section 2.8). The who output also lists login time and other information -- but you can cut that off with a command like cut (Section 21.14):

>     % who | cut -c1-8
>     joe
>     lisa
>     franka
>     lisa
>     joe
>     mondo
>     joe
>     ...

Some users are logged on more than once. To get a unique list, use sort -u (Section 22.6). You're done. Just put the name-making command line between backquotes:

>     % mail `who | cut -c1-8 | sort -u`

If you aren't sure how this works, replace the command you want to run with echo (Section 26.5):

>     % echo `who | cut -c1-8 | sort -u`
>     bozo franka harpo joe lisa mondo

After using Unix for a while, you'll find that this is one of its most useful features. You'll find many situations where you use one command to generate a list of words, then put that command in backquotes and use it as an argument to something else. Sometimes you'll want to nest (Section 36.24) the backquotes -- this is where the bash, ksh, bash, and zsh `$( )` operators (which replace the opening and closing backquote, respectively) come in handy. There are some problems with command substitution, but you usually won't run into them.

This book has many, many examples of command substitution. Here are some of them: making unique filenames (Section 8.17), removing some files from a list (Section 14.18), setting your shell prompt (Section 4.6, Section 4.8, Section 4.14), and setting variables (Section 4.8, Section 36.23).

-- JP

Copyright © 2003 O'Reilly & Associates. All rights reserved.