2012-04-05 4 views
56

Ich versuche, das Ergebnis von ls in anderen Befehlen verwenden, zu verwenden (zB Echo, rsync):Wie Shell-Befehle in Makefile

Aber ich bekomme:

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

I‘ habe versucht mit echo $$FILES, echo ${FILES} und echo $(FILES), ohne Glück.

Antwort

79

mit:

FILES = $(shell ls) 

unter all so zerklüftet, es ist ein Build-Befehl. Dies erweitert $(shell ls), dann versucht, den Befehl FILES ... auszuführen.

Wenn FILES soll eine make variabel sein, müssen diese Variablen außerhalb der Rezeptteil zugeordnet werden, zB:

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

Natürlich bedeutet dies, dass FILES wird „Ausgabe von ls“ gesetzt werden vor Ausführen eines der Befehle, die die TGZ-Dateien erstellen. (Obwohl als Kaz notes die Variablen schäumen wieder jedes Mal, so schließlich wird es die TGZ-Dateien enthält, einige Varianten machen FILES := ... dies zu vermeiden, für Effizienz und/oder Richtigkeit .)

Wenn FILES ist sollte ein Shell-Variable sein, Sie können es aber Sie müssen es in der Schale-ese tun, ohne Leerzeichen und zitiert:

all: 
     FILES="$(shell ls)" 

jedoch jede Zeile durch eine separate Shell ausgeführt wird, so wird diese Variable wird nicht bis zur nächsten Zeile überleben, also musst du es dann sofort verwenden:

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

ist alles ein bisschen albern, da die Schale ausdehnen * (und andere Ausdrücke Shell glob) für Sie an erster Stelle, so können Sie einfach:

 echo * 

als Shell-Befehl.

schließlich als allgemeine Regel (nicht wirklich für dieses Beispiel): als esperanto Hinweise in den Kommentaren, von ls die Ausgabe mit nicht absolut zuverlässig (einige Details hängen von Dateinamen und manchmal sogar die Version von ls, einig Versionen von ls versuchen, die Ausgabe in einigen Fällen zu bereinigen). So, wie l0b0 und idelic Hinweis, wenn Sie GNU verwenden, können Sie $(wildcard) und $(subst ...) verwenden, um alles innerhalb make selbst zu erreichen (Vermeidung von "seltsame Zeichen in Dateinamen" Probleme). (In sh Skripte, einschließlich der Rezepturanteil des Makefiles, ist eine andere Methode zu verwenden find ... -print0 | xargs -0 über Zuschnitten zu vermeiden Auslösung newlines, Steuerzeichen, und so weiter.)


The GNU Make documentation notes further that POSIX make added ::= assignment in 2012.Ich habe dafür weder einen schnellen Referenzlink zu einem POSIX-Dokument gefunden, noch weiß ich, welche make Varianten ::= Zuweisungen unterstützen, obwohl GNU make heute, mit der gleichen Bedeutung wie :=, also die Zuordnung gerade jetzt mit macht Erweiterung.

Beachten Sie, dass VAR := $(shell command args...) auch VAR != command args... in mehreren make Varianten geschrieben werden kann, einschließlich aller modernen GNU und BSD-Varianten, soweit ich weiß. Diese anderen Varianten haben nicht $(shell), so dass die Verwendung VAR != command args... ist besser in beiden kürzer und arbeiten in mehreren Varianten.

+0

Danke. Ich möchte einen ausgeklügelten Befehl verwenden ('ls' mit' sed' und cut, zum Beispiel) und dann die Ergebnisse in rsync und anderen Befehlen verwenden. Muss ich den langen Befehl immer wieder wiederholen? Kann ich die Ergebnisse nicht in einer internen Make-Variable speichern? –

+1

Gnu make könnte einen Weg dazu haben, aber ich habe es nie benutzt, und all die furchtbar komplizierten Makefiles, die wir benutzen, benutzen nur Shell-Variablen und riesige Einliner-Shell-Befehle mit ";" am Ende jeder Zeile wie benötigt. (Die Code-Kodierung kann hier nicht mit der Backslash-Sequenz funktionieren, hmm) – torek

+0

Außerdem sollten Sie anstelle von 'ls' (http://mywiki.wooled.org/ParsingLs) die [' Wildcard "make builtin" (https://www.gnu.org/software/make/manual/make.html#Wildcard-Function). – l0b0

29

Zusätzlich zu toreks Antwort: Eine Sache, die heraussticht, ist, dass Sie eine lazy-evaluated Makro-Zuweisung verwenden.

Wenn Sie mit GNU Make arbeiten, verwenden Sie die := Zuweisung anstelle von =. Diese Zuweisung bewirkt, dass die rechte Seite sofort expandiert und in der linken Variablen gespeichert wird.

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

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

Wenn die = Zuordnung verwenden, bedeutet dies, dass jedes einzelne Auftreten $(FILES) die $(shell ...) Syntax expandieren und damit den Shell-Befehl aufgerufen wird. Dies führt dazu, dass Ihr Auftrag langsamer läuft oder sogar überraschende Konsequenzen hat.

+0

Nun, da wir die Liste haben, wie wir über jedes Element in der Liste iterieren und einen Befehl darauf ausführen? Wie bauen oder testen? – anon58192932

+0

@ anon58192932 Diese spezielle Iteration, um einen Befehl auszuführen, wird normalerweise in einem Shell-Syntaxfragment in einem Build-Rezept ausgeführt, so dass es in der Shell statt in make vorkommt: 'for x in $ (FILES); tue Befehl $$ x; fertig. Beachten Sie das verdoppelte '$$', das ein einzelnes '$' an die Shell übergibt. Shell-Fragmente sind auch Einzeiler; Um mehrzeiligen Shell-Code zu schreiben, verwenden Sie eine Backslash-Fortsetzung, die von 'make' selbst verarbeitet und in eine Zeile gefaltet wird. Das bedeutet, Shell-Kommando-Trennzeichen sind Pflichtfelder. – Kaz