I'm reading a book about the make command and there is a paragraph:

If any of the prerequisites has an associated rule, make attempts to update those first. Next, the target file is considered. If any prerequisite is newer than the target, the target is remade by executing the commands. Each command line is passed to the shell and is executed in its own subshell.

Could you explain the concept of subshell used there and why is it necessary to use such a subshell?

  • Thank you for answers. Everybody's answer contributes to comprehension.
    – xralf
    Nov 1, 2011 at 9:23

4 Answers 4


make(1) itself does not know how to run shell commands. It could have been made to do so, but the Unix Way is to have well-separated concerns: make(1) knows how to build dependency graphs that determine what has to be made, and sh(1) knows how to run commands.

The point the author is trying to make there is that you must not write those command lines such that a later one depends on a former one, except through the filesystem. For example, this won't work:

sometarget: some.x list.y of-dependencies.z
    run-some-other-command $$foo

If this were a two-line shell script, the first command's output would be passed as an argument to the second command. But since each of these commands gets run in a separate sub-shell, the $foo variable's value gets lost after the first sub-shell exists, so there is nothing to pass to the first.

One way around this, as hinted above, is to use the filesystem:

sometarget: some.x list.y of-dependencies.z
    run-some-command-here > $(TMPFILE)
    run-some-other-command `cat $(TMPFILE)`

That stores the output of the first command in a persistent location so the second command can load the value up.

Another thing that trips make(1) newbies up sometimes is that constructs that are usually broken up into multiple lines for readability in a shell script have to be written on a single line or wrapped up into an external shell script when you do them in a Makefile. Loops are a good example; this doesn't work:

    for f in *.c
        munch-on $f

You have to use semicolons to get everything onto a single line instead:

    for f in *.c ; do munch-on $f ; done

For myself, whenever doing that gives me a command line longer than 80 characters or so, I move it into an external shell script so it's readable.

  • 1
    ++ for nice examples
    – ktf
    Oct 31, 2011 at 20:07
  • 1
    " It could have been made to do so" It could also have been made to pass all the commands to a single shell; do you have an explanation why this was not done?
    – Random832
    Nov 2, 2011 at 13:56
  • 1
    make(1) dates from 1977. Command line length limits were short and disks — hence temporary script files — were slow. Nov 2, 2011 at 15:19

Make isn't itself a shell, so it needs to execute them in a shell. The book is calling them "subshells" because make is itself (most likely) running in a shell. What the book is actually trying to get across there is that each command is given its own shell, so if, e.g. you want to export a variable on one line and use it on the next (or another subsequent) line, it won't work (the export ends with the shell it's used in, and the next line is run in a separate shell).


Old question but you could also run the command in the shell and get the output


OUTPUT = $(shell pwd)

    @echo $(OUTPUT)

What's meant is that every command line within a rule will be called in its own shell process. Reason is that make executes one command line and then wants to check it before calling the next command line.

One could think of other ways to call the commands but this is how make was designed back then.

Of course make could just fork()/exec() the commands but using a shell subprocess allows the user to use shell features, too.

  • Nor would fork/exec solve the problem whereby one line can not affect the execution environment of the next. Nov 1, 2011 at 0:10

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .