Daniel Gonzalez
Daniel Gonzalez

Reputation: 137

How to prevent evaluation of string in make / shell?

I have this makefile:

echo:
    echo "PASS=$(PASS)"

Which I invoke:

PASS='MYPA$$' make

Which shows me:

echo "PASS=MYPA$"
PASS=MYPA$

Somebody is evaluating $$ -> $.

Is this the shell? Not when inputting the value, since I use single-quotes, preventing the shell to evaluate it.

Maybe the shell invoked by make is doing this ...

Or is it maybe make itself?

How can I avoid it?

Upvotes: 4

Views: 1548

Answers (1)

randomir
randomir

Reputation: 18697

On make variables

It's better to think of make variables as macros, than as conventional variables (actually in some versions of make, variables are called macros). The reason is, each time a variable is referenced, it is expanded.

An example from the docs illustrates the standard recursively expanded variables behaviour:

foo = $(bar)
bar = $(ugh)
ugh = Huh?

all:
    echo $(foo)

# echoes: Huh?
# `$(foo)' expands to `$(bar)' which expands to `$(ugh)' which finally expands to `Huh?'

If you are using GNU make, one way to avoid further expansion is by using the simply expanded variables:

Simply expanded variables are defined by lines using := (see section Setting Variables). The value of a simply expanded variable is scanned once and for all, expanding any references to other variables and functions, when the variable is defined. The actual value of the simply expanded variable is the result of expanding the text that you write. It does not contain any references to other variables; it contains their values as of the time this variable was defined.

Although simply expanded variables behave more like variables in most programming languages, their sole usage wouldn't solve the problem of environment variables' expansion here because even the first reference var := $(PASS) would expand $$ from the PASS environment variable.

Avoiding expansion of environment variables

We can use the shell function in make to read our environment variable in shell (and not expand it in make):

expanded := $(shell echo "$$PASS")

test:
    echo 'PASS=$(expanded)'
    echo "PASS=$$PASS"

The shell function will execute echo "$PASS" in shell ($$ is expanded to $ by make when function is executed), and the result (the value of your shell variable PASS) will be stored in the make variable expanded. This variable can now be freely used elsewhere in make, without ever being further expanded.

The only processing make does on the result, before substituting it into the surrounding text, is to convert each newline or carriage-return / newline pair to a single space. It also removes the trailing (carriage-return and) newline, if it's the last thing in the result.

The example above illustrates how to use the make variable expanded and the environment variable PASS in your Makefile script:

$ PASS='MYPA$$' make
echo 'PASS=MYPA$$'
PASS=MYPA$$
echo "PASS=$PASS"
PASS=MYPA$$

Upvotes: 7

Related Questions