Gaston Sanchez
- Basics of pattern substitutions
- Use the
wildcard
function- Use the
%
operator
Let’s consider the example used in the previous two lessons. This
involves executing four R scripts—script1.R
, script2.R
, script3.R
,
script4.R
— in batch mode via the command R CMD BATCH --no-save
.
We’ve seen the naive option of writing a Makefile
like this:
.PHONY: all clean
all: script1.Rout script2.Rout script3.Rout script4.Rout
script1.Rout: script1.R
R CMD BATCH --no-save script1.R
script2.Rout: script2.R
R CMD BATCH --no-save script2.R
script3.Rout: script3.R
R CMD BATCH --no-save script3.R
script4.Rout: script4.R
R CMD BATCH --no-save script4.R
clean:
rm -f *.Rout
A makefile like the one above is obviously not very efficient. Instead,
we can define a variable rcmd
that refers to the R command in each
recipe.
Likewise, we can use a pattern rule for all the targets:
%.Rout: %.R
$(rcmd) $<
All the previous elements can be combined in the following makefile:
rcmd = R CMD BATCH --no-save
.PHONY: all clean
all: script1.Rout script2.Rout script3.Rout script4.Rout
%.Rout: %.R
$(rcmd) $<
clean:
rm -f *.Rout
As you can tell, the only part that still has some repetition has to do
with the prerequisites of the target all
(i.e. all the .Rout
files).
It would be nice if you could group all the .Rout
in one single
variable. Let’s see how to do this.
Remember Make’s wildcard function? We can use it to define a variable
with the list of input .R
files:
rfiles = $(wildcard *.R)
The good news is that we have a variable rfiles
that contains the
names of all the R script files. The problem now is how to take
advantage of rfiles
?
You may be tempted to do something like this (with some tragic consequences):
rcmd = R CMD BATCH --no-save
rfiles = $(wildcard *.R)
$(rfiles)out: $(rfiles)
$(rcmd) $(rfiles)
To create a variable that contains the name of the .Rout
files, we are
going to use the function patsubst (for pattern substitution).
The idea is to take advantage of the variable rfiles
—which has the
value: script1.R script2.R script3.R script4.R
—and define a pattern
of characters to be substituted.
The function patsubst
has the following usage:
$(patsubst pattern,replacement,text)
patsubst
finds whitespace-separated words in text
that match the
provided pattern
and replaces them with the specified replacement
.
A pattern
may contain a %
which acts as a wildcard, matching any
number of any characters within a word. If replacement
also contains a
%
, the %
is replaced by the text that matched the %
in pattern
.
Only the first %
in the pattern and replacement is treated this way;
any subsequent %
is unchanged.
In our example, we can use patsubst
to create the variable rout
like
thi:
rfiles = $(wildcard *.R)
rout = $(patsubst %.R,%.Rout,$(rfiles))
-
%.R
is the pattern to be match in the provided text -
$(rfiles)
is the provided text (i.e.script1.R script2.R script3.R script4.R
) -
%.Rout
is the replacement
In other words, patsubst
will look for the pattern .R
in rfiles
,
and will replace the pattern with .Rout
. This implies that rout
will
be a text formed by: script1.Rout script2.Rout script3.Rout script4.Rout
.
Having obtained the variable rout
with the list of output files, we
can complete the Makefile
as follows:
rcmd = R CMD BATCH --no-save
rfiles = $(wildcard *.R)
rout = $(patsubst %.R,%.Rout,$(rfiles))
all: $(rout)
%.Rout: %.R
$(rcmd) $<
clean:
rm -f *.Rout
Note that the variable rout
is used as the prerequisite of the target
all
. You don’t need to write a long list of prequisites for all
.
Functions for String Substitution and Analysis