How to use a for loop in make recipe

2019-04-25 08:38发布

问题:

I would like to use a loop to find some files and rename them:

  for i in `find $@  -name *_cu.*`;do mv $i "$(echo $i|sed s/_cu//)"
  done

This works in the shell. But how can I do this in a makefile recipe?

回答1:

There are two main things you need to know when putting non-trivial shell fragments into make recipes:

  1. Commands in the recipe are (of course!) executed one at a time, where command means "tab-prefixed line in the recipe", possibly spread over several makefile lines with backslashes.

    So your shell fragment has to be written all on one (possibly backslashed) line. Moreover it's effectively presented to the shell as a single line (the backslashed-newlines are not plain newlines so are not used as command terminators by the shell), so must be syntactically correct as such.

  2. Both shell variables and make variables are introduced by dollar signs ($@, $i), so you need to hide your shell variables from make by writing them as $$i. (More precisely, any dollar sign you want to be seen by the shell must be escaped from make by writing it as $$.)

Normally in a shell script you would write separate commands on separate lines, but here you effectively only get a single line so must separate the individual shell commands with semicolons instead. Putting all this together for your example produces:

foo: bar
    for i in `find $@  -name *_cu.*`; do mv $$i "$$(echo $$i|sed s/_cu//)"; done

or equivalently:

foo: bar
    for i in `find $@  -name *_cu.*`; do      \
        mv $$i "$$(echo $$i|sed s/_cu//)";    \
    done

Notice that the latter, even though it's laid out readably on several lines, requires the same careful use of semicolons to keep the shell happy.



回答2:

I found this useful, trying to use for loops to build multiple files:

PROGRAMS = foo bar other

.PHONY all
all: $(PROGRAMS)
$(PROGRAMS):
    gcc -o $@ $@.c

It will compile foo.c, bar.c, other.c into foor bar other executables