2012-04-05 4 views
56

Sto cercando di utilizzare il risultato di ls in altri comandi (ad esempio eco, rsync):Come utilizzare i comandi della shell in Makefile

ma ottengo:

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

I' Ho provato a utilizzare echo $$FILES, echo ${FILES} e echo $(FILES), senza fortuna.

risposta

79

Con:

FILES = $(shell ls) 

rientrato sotto all del genere, si tratta di un comando di generazione. Quindi questo si espande $(shell ls), quindi tenta di eseguire il comando FILES ....

Se FILES si suppone essere una variabile make, queste variabili devono essere assegnati al di fuori della porzione ricetta, ad esempio:

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

Naturalmente, questo significa che FILES verrà impostato su "output ls" prima di eseguendo uno qualsiasi dei comandi che creano i file .tgz. (Anche se, come Kaz notes la variabile è ritesero ogni volta, così alla fine si includono i file .tgz, alcuni rendere varianti hanno FILES := ... per evitare questo, per l'efficienza e/o correttezza .)

Se FILES è dovrebbe essere una variabile di shell, è possibile impostare, ma è necessario farlo in guscio-ese, senza spazi, e citato:

all: 
     FILES="$(shell ls)" 

Tuttavia, ogni linea è gestito da un guscio separato, in modo tale variabile non sopravviverà alla riga successiva, quindi è necessario utilizzarlo immediatamente:

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

Questo è tutto un po 'sciocco in quanto il guscio si espanderà * (e altre espressioni di shell glob) per voi in primo luogo, in modo da poter semplicemente:

 echo * 

come comando di shell.

Infine, come regola generale (in realtà non applicabile a questo esempio): come esperanto note nei commenti, utilizzando l'uscita dal ls non è completamente affidabile (alcuni dettagli dipendono i nomi dei file e, talvolta, anche la versione del ls, alcuni versioni di ls tentano di disinfettare l'output in alcuni casi). Pertanto, come l0b0 e idelic note, se si utilizza GNU make è possibile utilizzare $(wildcard) e $(subst ...) per eseguire tutto all'interno di make stesso (evitando problemi di "caratteri strani nel nome del file"). (In sh script, tra la porzione di ricetta makefiles, un altro metodo è quello di utilizzare find ... -print0 | xargs -0 per evitare inciampare sbozzati, a capo, caratteri di controllo, ecc.)


The GNU Make documentation notes further that POSIX make added ::= assignment in 2012.Non ho trovato un collegamento di riferimento rapido a un documento POSIX per questo, né so a maniere quali varianti di make supportano l'assegnazione ::=, sebbene GNU make lo faccia oggi, con lo stesso significato di :=, cioè, esegui il compito adesso con espansione.

noti che VAR := $(shell command args...) può anche essere scritto in diversi VAR != command args...make varianti, compresi tutti GNU moderno e BSD varianti per quanto ne so. Queste altre varianti non hanno $(shell) quindi l'utilizzo di VAR != command args... è superiore in entrambi i casi in cui sono più corti e che funzionano in più varianti.

+0

Grazie. Voglio usare un comando elaborato ('ls' con' sed' e tagliare, per esempio) e poi usare i risultati in rsync e altri comandi. Devo ripetere il lungo comando più e più volte? Non posso memorizzare i risultati in una variabile Make interna? –

+1

Gnu make potrebbe avere un modo per farlo, ma non l'ho mai usato, e tutti i makefile orribilmente complicati che usiamo usano solo variabili di shell e giganteschi comandi shell con un solo liner costruiti con "; \" alla fine di ogni riga come necessario. (non è possibile ottenere la codifica del codice per lavorare con la sequenza backslash qui, hmm) – torek

+0

Inoltre, [invece di 'ls'] (http://mywiki.wooledge.org/ParsingLs) si vorrà usare il [' wildcard' make builtin] (https://www.gnu.org/software/make/manual/make.html#Wildcard-Function). – l0b0

29

Inoltre, oltre alla risposta di torek: una cosa che spicca è che si sta utilizzando un macro assegnamento ponderato.

Se si utilizza GNU Make, utilizzare l'assegnazione := anziché =. Questo incarico fa sì che il lato destro sia espanso immediatamente e memorizzato nella variabile a sinistra.

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

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

Se si utilizza l'assegnazione =, significa che ogni singola occorrenza di $(FILES) sarà espandendo la sintassi $(shell ...) e invocando quindi il comando shell. Ciò renderà più lento il tuo lavoro, o avrà anche conseguenze sorprendenti.

+0

Ora che abbiamo la lista come eseguiamo iterazioni su ogni elemento della lista ed eseguiamo un comando su di esso? Come costruire o testare? – anon58192932

+0

@ anon58192932 Questa specifica iterazione, per eseguire un comando, viene in genere eseguita in un frammento di sintassi della shell in una ricetta di build, quindi si verifica nella shell, anziché in make: 'for x in $ (FILES); comando $ $$; done'. Notare il '$$ 'raddoppiato che passa un singolo' $ 'alla shell. Inoltre, i frammenti di shell sono one-liner; per scrivere codice shell multi-linea, si usa la continuazione backslash che viene elaborata da 'make' stessa e piegata in una riga. Il che significa che il punto e virgola che separa la shell è obbligatorio. – Kaz