0

Indirect variable setting in bash can be done like so:

#!/bin/bash
foo=bar
ind=foo
echo ${!ind}   # prints `bar'

Trying to run the same as a (GNU) Makefile recipe

# Makefile
test:
    foo=bar
    ind=foo
    echo $$\{!ind\}
    echo $${!ind}

both with and without escaping the {} characters fails with the following message:

foo=bar
ind=foo
echo $\{!ind\}
${!ind}
echo ${!ind}
/bin/sh: 1: Bad substitution
/tmp/Makefile:2: recipe for target 'test' failed
make: *** [test] Error 2

The problem is probably some missing/wrong escaping of Makefile's special characters.

Or it could be the order/timing of variable expansion, see make's secondary expansion.

Any ideas?

If it matters, this is bash 4.4.12(1)-release and make GNU Make 4.1

  • Related: [Error "/bin/sh: 1: Syntax error: "(" unexpected" in Makefile](https://unix.stackexchange.com/questions/521965/error-bin-sh-1-syntax-error-unexpected-in-makefile) – steeldriver Jan 17 '23 at 13:53
  • 1
    Every line in a Makefile is running in a different shell. Setting a shell variable is not preserved. – choroba Jan 17 '23 at 13:57

2 Answers2

0

You guys are just incredible. Less than ten minutes after I posted my question, you led me to the correct solution, which is:

# Makefile
SHELL=/bin/bash
test:
    foo=bar; \
    ind=foo; \
    echo $${!ind}

Thank you steeldriver, thank you choroba.

0

There are two problems here:

  1. The default shell used for recipes is a sh, so you need to use SHELL=/bin/bash for the recipe to even understand syntax like ${!ind}
  2. Each line in the recipe is executed by a separate shell, so if you define a variable in a recipe it is lost for the next line of the same recipe. This is solved by a .ONESHELL special target

So the working example looks like this:

# Makefile
.ONESHELL:
SHELL=/bin/bash
test:
    foo=bar
    ind=foo
    echo $$\{!ind\}
    echo $${!ind}

For a more detailed (and official) explanation, go to https://www.gnu.org/software/make/manual/make.html#Execution

White Owl
  • 4,511
  • 1
  • 4
  • 15