Iterar matriz associativa de bash no Makefile

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

Considere o seguinte script de shell:

#!/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

Estou tentando fazer o equivalente em um 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

Eu não realmente quero touch dos arquivos; Estou fazendo isso porque não posso @echo - o @ não será visto como estando no começo da linha porque estou em um loop. Ou é o que parece estar acontecendo.

De qualquer forma, o ponto é que o loop não parece estar rodando, daí a touch / echo business. O conteúdo do script de shell acima é exatamente o que make ecoa no terminal. Eu adicionei o shebang e corri como um teste de sanidade - funciona como um encanto.

Usar uma matriz regular funciona bem:

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

No entanto, eu preciso desses códigos (10, 11, etc.) também.

Alguém tem uma visão disso?

Embora eu não exija, eu também gostaria de saber como (ou se é possível) atribuir a variável PROVS no topo do arquivo enquanto também estiver usando "declare -A".

EDITAR: De alguma forma eu baguncei o exemplo do Makefile para que fossem apenas alguns comandos de shell embutidos, e não mais uma receita. Eu adicionei de volta a meta "foo:" para esclarecer.

    
por brian 28.09.2015 / 03:46

1 resposta

3

Se o seu trecho de código é adequadamente representativo, parece que você está digitando comandos Bash diretamente no seu Makefile e esperando que o Make os execute com o Bash. Não é assim que funciona. A sintaxe de um Makefile é totalmente diferente. Dentro de uma receita, você pode digitar comandos Bash; Cada linha separada em uma receita será executada em uma sub-shell separada. Então você precisa de pelo menos duas mudanças:

  • Seus comandos do shell precisam estar em um destino.
  • O declare precisa ser executado no mesmo shell que o loop; caso contrário você declare em uma instância de Bash, saia dessa e execute o loop em uma instância separada que não sabe nada sobre a perda de declare .

Aqui está uma refatoração simples do seu Makefile com essas alterações.

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: link

A convenção @ para executar um comando aplica-se silenciosamente a toda a linha de comando. Portanto, você pode colocá-lo antes de declare acima e, nesse caso, ele será retirado antes que toda a linha de comando seja enviada para o Bash. Em qualquer outro lugar, ele não será removido ou entendido, e obviamente causará um erro de sintaxe Bash no shell chamado.

(A obsessão com as regras @ é um antipadrão de qualquer maneira. Execute com make -s se você não quiser ver a saída; encerrar make só tornará mais difícil depurar suas regras.)

    
por 28.09.2015 / 06:36