Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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?

share|improve this question
    
Thank you for answers. Everybody's answer contributes to comprehension. –  xralf Nov 1 '11 at 9:23

3 Answers 3

up vote 6 down vote accepted

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
    foo=`run-some-command-here`
    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:

TMPDIR=/tmp
TMPFILE=$(TMPDIR)/example.tmp
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:

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

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

someutilitytarget:
    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.

share|improve this answer
    
++ for nice examples –  ktf Oct 31 '11 at 20:07
    
" 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 '11 at 13:56
    
make(1) dates from 1977. Command line length limits were short and disks — hence temporary script files — were slow. –  Warren Young Nov 2 '11 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).

share|improve this answer

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.

share|improve this answer
    
Nor would fork/exec solve the problem whereby one line can not affect the execution environment of the next. –  dmckee Nov 1 '11 at 0:10

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.