Como posso usar a sintaxe do Bash nos destinos do Makefile?

208

Costumo achar a sintaxe do Bash muito útil, por exemplo, substituição de processos como em diff <(sort file1) <(sort file2).

É possível usar esses comandos do Bash em um Makefile? Estou pensando em algo assim:

file-differences:
    diff <(sort file1) <(sort file2) > $@

No meu GNU Make 3.80, isso causará um erro, pois ele usa o shellinvés de bashpara executar os comandos.

Frank
fonte
Esse foi exatamente o meu problema. Levei pelo menos uma hora para encontrar essa pergunta! Deixo minha mensagem de erro aqui para que futuros leitores possam encontrá-la: /bin/sh: -c: line 0: syntax error near unexpected token (''
David

Respostas:

380

Na documentação do GNU Make,

5.3.1 Choosing the Shell
------------------------

The program used as the shell is taken from the variable `SHELL'.  If
this variable is not set in your makefile, the program `/bin/sh' is
used as the shell.

Então coloque SHELL := /bin/bashno topo do seu makefile e você deve estar pronto.

BTW: Você também pode fazer isso para um destino, pelo menos para o GNU Make. Cada destino pode ter suas próprias atribuições de variáveis, assim:

all: a b

a:
    @echo "a is $$0"

b: SHELL:=/bin/bash   # HERE: this is setting the shell for b only
b:
    @echo "b is $$0"

Isso será impresso:

a is /bin/sh
b is /bin/bash

Consulte "Valores variáveis ​​de destino específicos" na documentação para obter mais detalhes. Essa linha pode ir a qualquer lugar no Makefile, não precisa ser imediatamente antes do destino.

derobert
fonte
43
500 recompensas à espera de uma cotação de man. Fale sobre horários. : P
SiddharthaRT
3
@inLoveWithPython Bem, infona verdade, mas acho que realmente ajudou Andy. Eu sei que tive dias assim ...
derobert 3/12/12
3
em caso de dúvida, @derobert quis dizer literalmente: SHELL=/bin/bashcomo a primeira linha do Makefile (ou logo após o comentário).
Yauhen Yakimovich
1
Graças @derobert resolveu o meu problema em stackoverflow.com/questions/26806832/...
Chandan Choudhury
2
É possível alterar a variável SHELL apenas para um alvo específico, mas deixar os outros intocados?
perfil completo de antred
18

Você pode ligar bashdiretamente, use a -cbandeira:

bash -c "diff <(sort file1) <(sort file2) > $@"

Obviamente, talvez você não consiga redirecionar para a variável $ @, mas quando tentei fazer isso, recebi -bash: $@: ambiguous redirectuma mensagem de erro; portanto, talvez você queira investigar isso antes de entrar nessa (embora eu usando o bash 3.2. algo, então talvez o seu funcione de maneira diferente).

Chris Lutz
fonte
4

Se a portabilidade for importante, talvez você não queira depender de um shell específico no seu Makefile. Nem todos os ambientes têm o bash disponível.

Menno Smits
fonte
4

Você pode chamar o bash diretamente no seu Makefile em vez de usar o shell padrão:

bash -c "ls -al"

ao invés de:

ls -al
paxdiablo
fonte
1
Observe que makeignora o valor da variável de ambiente SHELL.
choroba
2

Existe uma maneira de fazer isso sem definir explicitamente sua variável SHELL para apontar para o bash. Isso pode ser útil se você tiver muitos makefiles, pois o SHELL não é herdado pelos makefiles subsequentes ou retirado do ambiente. Você também precisa ter certeza de que qualquer pessoa que compila seu código configura seu sistema dessa maneira.

Se você executar sudo dpkg-reconfigure dashe responder 'não' ao prompt, seu sistema não usará o traço como shell padrão. Em seguida, ele apontará para o bash (pelo menos no Ubuntu). Observe que usar o traço como shell do sistema é um pouco mais eficiente.

Bill G
fonte
1
Quando chamado sob o nome sh, o bash é executado no modo de compatibilidade ( set -o posix). A funcionalidade que o OP está tentando usar, substituição de processo, não está disponível neste modo.
Charles Duffy
1

Uma maneira que também funciona é colocá-lo dessa maneira na primeira linha do seu destino:

your-target: $(eval SHELL:=/bin/bash)
    @echo "here shell is $$0"
Nicolas Marshall
fonte