it-swarm.dev

Cómo usar los comandos de Shell en Makefile

Estoy tratando de usar el resultado de ls en otros comandos (por ejemplo, echo, rsync):

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

Pero me sale:

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

He intentado usar echo $$FILES, echo ${FILES} y echo $(FILES), sin suerte.

75
Adam Matan

Con:

FILES = $(Shell ls)

con sangría debajo de all así, es un comando de compilación. Así que esto expande $(Shell ls), luego intenta ejecutar el comando FILES ....

Si se supone que FILES es una variable make, estas variables deben asignarse fuera de la parte de la receta, por ejemplo:

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

Por supuesto, eso significa que FILES se configurará como "salida de ls" antes de ejecutando cualquiera de los comandos que crean los archivos .tgz. (Aunque como Notas de Kaz la variable se vuelve a expandir cada vez, por lo que eventualmente incluirá los archivos .tgz; algunas variantes de la marca tienen FILES := ... para evitar esto, para mayor eficiencia y/o corrección.1)

Si se supone que FILES es una variable de Shell, puede configurarlo, pero debe hacerlo en Shell-ese, sin espacios, y entre comillas:

all:
        FILES="$(Shell ls)"

Sin embargo, cada línea se ejecuta por un Shell separado, por lo que esta variable no sobrevivirá a la siguiente línea, por lo que debe usarla inmediatamente:

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

Todo esto es un poco tonto ya que el Shell expandirá * (y otras expresiones globales de Shell) para usted en primer lugar, por lo que puede simplemente:

        echo *

como su comando de Shell.

Finalmente, como regla general (no se puede aplicar realmente a este ejemplo): como esperanto observa en los comentarios, usar la salida de ls no es completamente confiable (algunos detalles dependen de los nombres de los archivos y algunas veces incluso la versión de ls; algunos las versiones de ls intentan sanear la salida en algunos casos). Por lo tanto, como l0b0 y idelic note, si está usando GNU puede usar $(wildcard) y $(subst ...) para lograr todo dentro de make (evitando cualquier "carácter extraño en el nombre del archivo " cuestiones). (En los scripts sh, incluida la parte de la receta de makefiles, otro método es usar find ... -print0 | xargs -0 para evitar tropezar con espacios en blanco, nuevas líneas, caracteres de control, etc.)


1El GNU Hacer notas de documentación más allá de que POSIX realice la asignación de ::= agregado en 2012 . No he encontrado un enlace de referencia rápida a un documento POSIX para esto, ni tampoco conozco de antemano qué make variantes admiten la asignación ::=, aunque GNU make hace hoy, con el mismo significado que :=, es decir, haz la tarea ahora mismo con expansión.

Tenga en cuenta que VAR := $(Shell command args...) también se puede deletrear VAR != command args... en varias variantes de make, incluidas todas las variantes modernas GNU y BSD, que yo sepa. Estas otras variantes no tienen $(Shell), por lo que usar VAR != command args... es superior en cuanto a que es más corto y trabaja en más variantes.

113
torek

Además, además de la respuesta de torek: una cosa que se destaca es que estás usando una asignación de macro evaluada perezosamente.

Si estás en GNU Make, usa la asignación := en lugar de =. Esta asignación hace que el lado derecho se expanda inmediatamente y se almacene en la variable de la mano izquierda.

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

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

Si utiliza la asignación =, significa que cada aparición de $(FILES) expandirá la sintaxis $(Shell ...) y, por lo tanto, invocará el comando de Shell. Esto hará que su trabajo sea más lento o incluso tenga algunas consecuencias sorprendentes.

39
Kaz