2012-04-05 13 views
56

Estoy tratando de utilizar el resultado de ls en otros comandos (por ejemplo, eco, rsync):Cómo utilizar los comandos de shell en el Makefile

pero me sale:

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

I' he intentado usar echo $$FILES, echo ${FILES} y echo $(FILES), sin suerte.

Respuesta

79

Con:

FILES = $(shell ls) 

sangría debajo all así, se trata de un comando de construcción. Entonces esto expande $(shell ls), luego intenta ejecutar el comando FILES ....

Si FILES se supone que es una variable make, estas variables deben ser asignados fuera de la porción de la receta, por ejemplo:

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

Por supuesto, eso significa que FILES se establecerá en "salida de ls" antes de ejecutando cualquiera de los comandos que crean los archivos .tgz. (Aunque como Kaz notes la variable se vuelve a ampliarse cada vez, por lo que finalmente se incluyen los archivos .tgz, algunos que tienen variantes FILES := ... Para evitar esto, la eficiencia y/o corrección .)

Si es FILES supone que es una variable de entorno, se puede establecer pero hay que hacerlo con cáscara-ese, sin espacios, y citó:

all: 
     FILES="$(shell ls)" 

sin embargo, cada línea está dirigido por una cáscara por separado, por lo que esta variable no sobrevivirá a la próxima línea, por lo que debe usarlo inmediatamente:

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

todo esto es un poco tonto ya que la cáscara se expandirá * (y otras expresiones concha glob) para usted, en primer lugar, lo que sólo puede:

 echo * 

como su comando shell.

Por último, como regla general (no es realmente aplicable a este ejemplo): como esperanto notas en los comentarios, utilizando la salida de ls no es completamente fiable (algunos detalles dependen de nombres de archivo y, a veces, incluso la versión de ls, algunos las versiones de ls intentan desinfectar la salida en algunos casos). Por lo tanto, como l0b0 y idelic tenga en cuenta que si usa GNU puede usar $(wildcard) y $(subst ...) para lograr todo dentro de make (evitando cualquier problema de "caracteres extraños en el nombre de archivo"). (En sh secuencias de comandos, incluyendo la parte de receta de makefiles, otro método es utilizar find ... -print0 | xargs -0 para evitar tropezar con los espacios en blanco, saltos de línea, caracteres de control, y así sucesivamente.)


The GNU Make documentation notes further that POSIX make added ::= assignment in 2012.No he encontrado un enlace de referencia rápido para un documento POSIX para esto, ni sé de forma remota qué variantes de make admiten la asignación ::=, aunque la fabricación GNU sí lo hace hoy, con el mismo significado que :=, es decir, hacer la asignación ahora mismo con expansión.

Tenga en cuenta que VAR := $(shell command args...) también se puede escribir VAR != command args... en varias variantes make, incluidas todas las variantes modernas de GNU y BSD hasta donde yo sé. Estas otras variantes no tienen $(shell) por lo que el uso de VAR != command args... es superior en ambos siendo más cortos y trabajando en más variantes.

+0

Gracias. Quiero usar un comando elaborado ('ls' con' sed' y cut, por ejemplo) y luego usar los resultados en rsync y otros comandos. ¿Debo repetir el largo comando una y otra vez? ¿No puedo almacenar los resultados en una variable interna de Make? –

+1

Gnu make podría tener una forma de hacer eso, pero nunca lo he usado, y todos los makefiles horriblemente complicados que usamos solo usan variables de shell y comandos de shell de una línea gigantes construidos con "; \" al final de cada línea según sea necesario. (no se puede obtener la codificación del código para que funcione con la secuencia de barra invertida aquí, hmm) – torek

+0

Además, [en lugar de 'ls'] (http://mywiki.wooledge.org/ParsingLs) querrá usar la opción [' wildcard' make built]] (https://www.gnu.org/software/make/manual/make.html#Wildcard-Function). – l0b0

29

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

Si está en GNU Make, use 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 ocurrencia de $(FILES) va a ampliar la sintaxis $(shell ...) e invocando así el comando shell. Esto hará que su trabajo funcione más lento, o incluso que tenga algunas consecuencias sorprendentes.

+0

Ahora que tenemos la lista, ¿cómo iteramos sobre cada elemento en la lista y ejecutamos un comando en él? Tales como construir o probar? – anon58192932

+0

@ anon58192932 Esa iteración específica, para ejecutar un comando, generalmente se realiza en un fragmento de sintaxis de shell en una receta de compilación, por lo que ocurre en el shell, en lugar de en make: 'para x en $ (ARCHIVOS); do comando $$ x; hecho'. Tenga en cuenta el doblado '$$' que pasa un solo '$' al shell. Además, los fragmentos de concha son de una sola línea; para escribir código de shell multilínea, utiliza la continuación de barra invertida procesada por 'make' y plegada en una línea. Lo que significa que los puntos y comas que separan el comando shell son obligatorios. – Kaz

Cuestiones relacionadas