web-dev-qa-db-fra.com

Comment utiliser les commandes de 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 reçois:

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} Et echo $(FILES), sans succès.

84
Adam Matan

Avec:

FILES = $(Shell ls)

mis en retrait sous all comme ça, c'est une commande de construction. Donc cela élargit $(Shell ls), puis essaie d'exécuter la commande FILES ....

Si FILES est supposé être une variable make, ces variables doivent être affectées en dehors de la portion de recette, par exemple:

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

Bien sûr, cela signifie que FILES sera réglé sur "sortie de ls" avant que n'exécute aucune des commandes. qui créent les fichiers .tgz. (Bien que notes Kaz la variable est ré-développée à chaque fois, elle inclura donc éventuellement les fichiers .tgz; certaines variantes de make ont FILES := ... Pour éviter cela, par souci d'efficacité et/ou exactitude.1)

Si FILES est supposé être une variable Shell, vous pouvez la définir, mais vous devez le faire dans Shell-ese, sans espaces, et cite:

all:
        FILES="$(Shell ls)"

Cependant, chaque ligne est exécutée par un shell distinct. Cette variable ne survivra donc pas à la ligne suivante. Vous devez donc l'utiliser immédiatement:

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

Tout cela est un peu ridicule puisque le shell développera pour vous * (Et d'autres expressions globales du shell), vous pouvez donc simplement:

        echo *

comme votre commande Shell.

Enfin, en règle générale (ne s'applique pas vraiment à cet exemple): as esperanto note dans les commentaires, utiliser le résultat de ls n’est pas totalement fiable (certains détails dépendent des noms de fichiers et parfois même la version de ls; certaines versions de ls tentent d’assainir la sortie dans certains cas). Ainsi, comme l0b et idélique remarque, si vous utilisez GNU vous pouvez utiliser $(wildcard) et $(subst ...) pour tout accomplir à l'intérieur de make lui-même (en évitant les problèmes de "caractères étranges dans le nom de fichier"). (Dans sh scripts, y compris la partie recette de makefiles, une autre méthode utiliser find ... -print0 | xargs -0 pour éviter de trébucher sur les blancs, les nouvelles lignes, les caractères de contrôle, etc.)


1Le GNU Complétez les remarques de la documentation en indiquant que POSIX a ajouté une affectation ::= En 2012 . Je n'ai pas trouvé de lien de référence rapide vers un document POSIX pour cela. , je ne connais pas non plus à l'avance laquelle make variantes supporte l'assignation ::=, bien que GNU make le fasse aujourd'hui, avec la même signification que :=, c’est-à-dire faire la tâche en ce moment avec expansion.

Notez que VAR := $(Shell command args...) peut également être orthographié VAR != command args... Dans plusieurs make variantes, y compris toutes les variantes modernes GNU et BSD). Ces autres variantes n’ayant pas $(Shell), l’utilisation de VAR != command args... Est supérieure car elle est plus courte et et fonctionne plus variantes.

125
torek

En outre, en plus de la réponse de torek: une chose qui se démarque est que vous utilisez une affectation de macro évaluée paresseuse.

Si vous êtes sur GNU Make, utilisez l'affectation := Au lieu de =. Cette assignation entraîne le développement immédiat du côté droit, puis 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 l'affectation =, Cela signifie que chaque occurrence de $(FILES) développera la syntaxe $(Shell ...) et invoquera ainsi la commande Shell. Cela ralentira votre travail, voire aura des conséquences surprenantes.

45
Kaz