Comment puis-je utiliser la syntaxe Bash dans les cibles Makefile?

209

Je trouve souvent la syntaxe Bash très utile, par exemple la substitution de processus comme dans diff <(sort file1) <(sort file2).

Est-il possible d'utiliser de telles commandes Bash dans un Makefile? Je pense à quelque chose comme ça:

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

Dans mon GNU Make 3.80, cela donnera une erreur car il utilise le shellau lieu de bashpour exécuter les commandes.

Franc
la source
C'était exactement mon problème, il m'a fallu au moins une heure pour trouver cette question! Je laisse mon message d'erreur ici afin que les futurs lecteurs puissent le trouver: /bin/sh: -c: line 0: syntax error near unexpected token (''
David

Réponses:

381

Dans la documentation 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.

Alors mettez SHELL := /bin/bashen haut de votre makefile, et vous devriez être prêt à partir.

BTW: Vous pouvez également le faire pour une cible, au moins pour GNU Make. Chaque cible peut avoir ses propres affectations de variables, comme ceci:

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"

Cela imprimera:

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

Voir «Valeurs variables spécifiques à la cible» dans la documentation pour plus de détails. Cette ligne peut aller n'importe où dans le Makefile, elle ne doit pas être immédiatement avant la cible.

derobert
la source
43
500 primes en attente d'un devis man. Parlez des horaires. : P
SiddharthaRT
3
@inLoveWithPython Eh bien, infoen fait, mais je suppose que cela a vraiment aidé Andy. Je sais que j'ai eu des jours comme ça ...
derobert
3
en cas de doute, @derobert signifie littéralement: SHELL=/bin/bashcomme la première ligne du Makefile (ou juste après le commentaire).
Yauhen Yakimovich
1
Merci @derobert a résolu mon problème dans stackoverflow.com/questions/26806832/…
Chandan Choudhury
2
Est-il possible de changer la variable SHELL juste pour une cible make particulière mais de laisser les autres intactes?
entrée le
18

Vous pouvez appeler bashdirectement, utilisez le -cdrapeau:

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

Bien sûr, vous ne pourrez peut-être pas rediriger vers la variable $ @, mais quand j'ai essayé de le faire, j'ai reçu -bash: $@: ambiguous redirectun message d'erreur, donc vous voudrez peut-être examiner cela avant de vous lancer trop dans ce (bien que je sois en utilisant bash 3.2.quelque chose, alors peut-être que le vôtre fonctionne différemment).

Chris Lutz
la source
4

Si la portabilité est importante, vous ne voudrez peut-être pas dépendre d'un shell spécifique dans votre Makefile. Tous les environnements ne disposent pas de bash.

Menno Smits
la source
4

Vous pouvez appeler bash directement dans votre Makefile au lieu d'utiliser le shell par défaut:

bash -c "ls -al"

au lieu de:

ls -al
paxdiablo
la source
1
Notez que makeignore la valeur de la variable d'environnement SHELL.
choroba
2

Il existe un moyen de le faire sans définir explicitement votre variable SHELL pour pointer vers bash. Cela peut être utile si vous avez plusieurs makefiles car SHELL n'est pas hérité par les makefiles suivants ou pris dans l'environnement. Vous devez également vous assurer que toute personne qui compile votre code configure son système de cette façon.

Si vous exécutez sudo dpkg-reconfigure dashet répondez «non» à l'invite, votre système n'utilisera pas dash comme shell par défaut. Il pointera alors vers bash (au moins dans Ubuntu). Notez que l'utilisation de dash comme shell de votre système est un peu plus efficace.

Bill G
la source
1
Lorsqu'il est appelé sous le nom sh, bash s'exécute en mode de compatibilité ( set -o posix). La fonctionnalité que l'OP essaie d'utiliser, la substitution de processus, n'est pas disponible dans ce mode.
Charles Duffy
1

Une façon qui fonctionne également est de le mettre de cette façon dans la première ligne de votre cible:

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