90 votes

Comment utiliser les commandes shell dans Makefile

J'essaie d'utiliser le résultat de ls dans d'autres commandes (par exemple, echo, rsync) :

all:
    <Building, creating some .tgz files - removed for clarity>
    FILES = $(shell ls)
    echo $(FILES)

Mais je comprends :

make
FILES = Makefile file1.tgz file2.tgz file3.tgz
make: FILES: No such file or directory
make: *** [all] Error 1

J'ai essayé d'utiliser echo $$FILES , echo ${FILES} y echo $(FILES) sans succès.

133voto

torek Points 25463

Avec :

FILES = $(shell ls)

en retrait en dessous all comme ça, c'est une commande de construction. Donc ça développe $(shell ls) puis essaie d'exécuter la commande FILES ... .

Si FILES est censé être un make ces variables doivent être affectées en dehors de la partie de la recette, par exemple :

FILES = $(shell ls)
all:
        echo $(FILES)

Bien sûr, cela signifie que FILES sera réglé sur "sortie de ls " avant en exécutant l'une des commandes qui créent les fichiers .tgz. (Bien qu'en tant que Notes de Kaz la variable est réexpansée à chaque fois, de sorte que finalement elle inclura les fichiers .tgz ; certaines variantes de make ont FILES := ... pour éviter cela, par souci d'efficacité et/ou de correction. 1 )

Si FILES est censée être une variable shell, vous pouvez la définir mais vous devez le faire en langage shell, sans espace, et entre guillemets :

all:
        FILES="$(shell ls)"

Cependant, chaque ligne est exécutée par un shell séparé, donc cette variable ne survivra pas à la ligne suivante, vous devez donc l'utiliser immédiatement :

        FILES="$(shell ls)"; echo $$FILES

Tout cela est un peu idiot puisque la coquille va se dilater * (et autres expressions globales de l'interpréteur de commandes) pour vous en premier lieu, afin que vous puissiez simplement :

        echo *

comme commande shell.

Enfin, à titre de règle générale (qui ne s'applique pas vraiment à cet exemple) : en tant que esperanto dans les commentaires, en utilisant la sortie de ls n'est pas complètement fiable (certains détails dépendent des noms de fichiers et parfois même de la version de ls ; certaines versions de ls pour tenter d'assainir la sortie dans certains cas). Ainsi, comme l0b0 y idiote note, si vous utilisez GNU make vous pouvez utiliser $(wildcard) y $(subst ...) pour tout accomplir à l'intérieur make lui-même (évitant tout problème de "caractères bizarres dans le nom du fichier"). (En sh y compris la partie recette des makefiles, une autre méthode consiste à utiliser find ... -print0 | xargs -0 pour éviter de trébucher sur les blancs, les nouvelles lignes, les caractères de contrôle, etc.)


1 La documentation de GNU Make note en outre que POSIX make ajoutait ::= mission en 2012 . Je n'ai pas trouvé de lien de référence rapide vers un document POSIX à ce sujet, et je ne sais pas non plus d'emblée quel document POSIX existe. make support des variantes ::= bien que GNU make le fasse aujourd'hui, avec la même signification que := c'est à dire, faire le devoir tout de suite avec expansion.

Notez que VAR := $(shell command args...) peut également s'écrire VAR != command args... dans plusieurs make y compris toutes les variantes modernes de GNU et BSD pour autant que je sache. Ces autres variantes n'ont pas $(shell) donc en utilisant VAR != command args... est supérieur en étant à la fois plus court y travailler dans plus de variantes.

47voto

Kaz Points 18072

En outre, en plus de la réponse de Torek, une chose qui ressort est que vous utilisez une affectation de macro évaluée paresseusement.

Si vous êtes sous GNU Make, utilisez la commande := au lieu de = . Cette affectation entraîne le développement immédiat de la partie droite et son stockage dans la variable de gauche.

FILES := $(shell ...)  # expand now; FILES is now the result of $(shell ...)

FILES = $(shell ...)   # expand later: FILES holds the syntax $(shell ...)

Si vous utilisez le = cela signifie que chaque occurrence unique de $(FILES) va étendre le $(shell ...) et ainsi invoquer la commande shell. Cela rendra votre travail make plus lent, ou aura même des conséquences surprenantes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X