5
$ bash -version
GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu)

Consider the following shell script:

#!/bin/bash

declare -A PROVS=( ["NL"]=10 ["PE"]=11 ["NS"]=12 ["NB"]=13 ["QC"]=24 ["ON"]=35 ["MB"]=46 ["SK"]=47 ["AB"]=48 ["BC"]=59 ["YK"]=60 ["NT"]=61 ["NU"]=62 )

for key in "${!PROVS[@]}" ; do \
  touch "foo_${key}_${PROVS[${key}]}" ; \
done

I'm attempting to do the equivalent in a Makefile:

SHELL := /bin/bash
.PHONY: foo
foo:
  declare -A PROVS=( ["NL"]=10 ["PE"]=11 ["NS"]=12 ["NB"]=13 ["QC"]=24 ["ON"]=35 ["MB"]=46 ["SK"]=47 ["AB"]=48 ["BC"]=59 ["YK"]=60 ["NT"]=61 ["NU"]=62 )

  for key in "$${!PROVS[@]}" ; do \
    touch "foo_$${key}_$${PROVS[$${key}]}" ; \
  done

I don't really want to touch the files; I'm doing this because I can't @echo -- the @ won't be seen as being at the beginning of the line because I'm in a loop. Or that's what seems to be happening.

Anyway, the point is that the loop doesn't appear to be running at all, hence the touch/echo business. The content of the shell script above is exactly what make echoes to the terminal. I added the shebang and ran it as a sanity check -- works like a charm.

Using a regular array works fine:

for prov in NL PE NS NB QC ON MB SK AB BC YK NT NU ; do \

However, I need those codes (10, 11, etc.) as well.

Anyone have insight to this?

Although I don't require it, I'd also like to know how (or if it's possible) to assign the PROVS variable at the top of the file while also using "declare -A".

EDIT: I'd somehow messed up the Makefile example so that it was just some inline shell commands, and no longer a recipe. I've added back the "foo:" target to clarify.

6

If your code excerpt is properly representative, it seems that you are typing Bash commands directly in your Makefile and expecting Make to execute them with Bash. That's not how it works. The syntax of a Makefile is entirely different. Within a recipe, you can type Bash commands; each separate line in a recipe will be executed in a separate sub-shell. So you need at least two changes:

  • Your shell commands need to be in a target.
  • The declare needs to run in the same shell as the loop; otherwise you declare in one Bash instance, then exit that, then run the loop in a separate instance which knows nothing about the now-lost declare.

Here is a simple refactoring of your Makefile with these changes.

SHELL=/bin/bash   # This is the standard compliant method

.PHONY: all
all:
    declare -A PROVS=( ["NL"]=10 ["PE"]=11 ["NS"]=12 ["NB"]=13 \
        ["QC"]=24 ["ON"]=35 ["MB"]=46 ["SK"]=47 ["AB"]=48 \
        ["BC"]=59 ["YK"]=60 ["NT"]=61 ["NU"]=62 )\
    ; for key in "$${!PROVS[@]}" ; do \
        touch "foo_$${key}_$${PROVS[$${key}]}" ; \
    done

Demo: http://ideone.com/t94AOB

The @ convention to run a command silently applies to the entire command line. Thus, you can put it before declare above, in which case it will be stripped off before the entire command line is submitted to Bash. Anywhere else, it will not be stripped or understood, and it will obviously cause a Bash syntax error in the called shell.

(The obsession with @ rules is an anti-pattern anyway. Run with make -s if you don't want to see the output; shutting up make will only make it harder to debug your rules.)

  • Though it often makes more sense to have make itself manage your files -- that way, you get the dependency tracking etc. which is why you are usually using make in the first place. – tripleee Sep 28 '15 at 8:38
  • @schily It is valid in GNU Make; since the OP is using this syntax, I assume that's what they are running. See also gnu.org/software/make/manual/html_node/Setting.html – tripleee Sep 28 '15 at 9:24
  • Advising people to use nonstandard and vendor specific "enhancements" where the official standardized methods do the same causes an unneeded vendor lock in. The important advantage of UNIX is that there is no vendor lock in as long as you write code follows the standard., so please avoid supporting a vendor lock in. – schily Sep 28 '15 at 9:37
  • 1
    @schily Seriously? I can add a caveat to this answer but I don't think it adds any particular value here. Gripe on the OP for using GNU Make if you really want to pick this particular battle. – tripleee Sep 28 '15 at 9:39
  • 1
    @schily Unless you want to propose a standards-compliant replacement for the associative array in the recipe as well, there's no point in complaining about the lack of portability in this Makefile. – chepner Sep 28 '15 at 18:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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