Es ist unklar, was Sie tun möchten, aber meiner Erfahrung nach makewollte ich das Verzeichnis nie so ändern. Vielleicht sollten Sie einen anderen Ansatz für Ihre Lösung ausprobieren?
P Shved
2
Es ist ein häufiger Anfängerfehler zu glauben, dass Ihr Verzeichnis wichtig ist. Für die meisten Dinge ist es nicht; cd dir; cmd filekann fast immer sinnvoller ausgedrückt werden als cmd dir/file.
Tripleee
34
Es ist ein häufiger Anfängerfehler zu glauben, dass Ihr derzeitiges Arbeitsverzeichnis keine Rolle spielt. Viele Programme, insbesondere Shell-Skripte, wurden unter .Berücksichtigung eines bestimmten Werts geschrieben. Es ist wahr, dass die meisten Tools so konzipiert sind, dass Sie Ihr pwd dafür nicht ändern müssen. Dies ist jedoch nicht immer der Fall, und ich halte es nicht für eine gute Idee, es als "Fehler" zu bezeichnen, um zu glauben, dass Ihr Verzeichnis wichtig sein könnte.
Evelyn Kokemoor
Tipp: Wenn Ihr Befehl cd sagt „Keine solche Datei oder das Verzeichnis“, auch wenn das (relative) Verzeichnis tut exist, überprüfen Sie, ob Ihre CDPATH Umgebungsvariable entweder leer oder enthält „“. Make führt Befehle mit "sh" aus, die nur dann einen relativen Pfad über CDPATH finden, wenn dieser festgelegt ist. Dies steht im Gegensatz zu Bash, das es versuchen wird. vor Rücksprache mit CDPATH.
Denis Howe
Antworten:
620
Es führt den Befehl tatsächlich aus und ändert das Verzeichnis in some_directory. Dies wird jedoch in einer Unterprozess-Shell ausgeführt und wirkt sich weder auf make noch auf die Shell aus, von der aus Sie arbeiten.
Wenn Sie mehr Aufgaben ausführen möchten some_directory, müssen Sie ein Semikolon hinzufügen und auch die anderen Befehle anhängen. Beachten Sie, dass Sie keine Zeilenumbrüche verwenden können, da diese von make als Ende der Regel interpretiert werden. Daher müssen alle Zeilenumbrüche, die Sie aus Gründen der Übersichtlichkeit verwenden, durch einen Backslash maskiert werden.
Zum Beispiel:
all:
cd some_dir; echo "I'm in some_dir"; \
gcc -Wall -o myTest myTest.c
Beachten Sie auch, dass das Semikolon zwischen jedem Befehl erforderlich ist, obwohl Sie einen Backslash und eine Newline hinzufügen. Dies liegt an der Tatsache, dass die gesamte Zeichenfolge von der Shell als einzelne Zeile analysiert wird. Wie in den Kommentaren erwähnt, sollten Sie '&&' verwenden, um Befehle zu verbinden. Dies bedeutet, dass sie nur ausgeführt werden, wenn der vorhergehende Befehl erfolgreich war.
all:
cd some_dir && echo "I'm in some_dir" && \
gcc -Wall -o myTest myTest.c
Dies ist besonders wichtig, wenn Sie zerstörerische Arbeiten ausführen, z. B. Aufräumen, da Sie sonst das falsche Material zerstören, falls dies cdaus irgendeinem Grund fehlschlägt.
Eine häufige Verwendung ist jedoch das Aufrufen von make im Unterverzeichnis, in das Sie möglicherweise schauen möchten. Hierfür gibt es eine Befehlszeilenoption, sodass Sie sich nicht selbst anrufen müssen cd, sodass Ihre Regel so aussehen würde
all:
$(MAKE) -C some_dir all
das wird sich in some_dirdas Makefiledort mit dem Ziel "all" ändern und ausführen . Verwenden Sie als bewährte Methode, $(MAKE)anstatt makedirekt aufzurufen , da Sie die richtige make-Instanz aufrufen müssen (wenn Sie beispielsweise eine spezielle make-Version für Ihre Build-Umgebung verwenden) und beim Ausführen ein etwas anderes Verhalten bereitstellen mit bestimmten Schaltern, wie z -t.
Machen Sie für den Datensatz immer den Befehl, den er ausführt (sofern er nicht ausdrücklich unterdrückt wird), auch wenn er keine Ausgabe hat, was Sie sehen.
Nun, nicht immer . Um das Echo zu unterdrücken, setzen Sie einfach @ am Anfang der Zeile.
Beta
2
@Beta: Nun ja, und ein Bindestrich-Präfix ignoriert auch den Fehlerstatus. Vielleicht wurde ich ein wenig mitgerissen, ich wollte darauf hinweisen, dass make den Befehl wiedergibt, unabhängig davon, um welche Art von Befehl es sich handelt. In diesem Fall handelt es sich um einen Befehl ohne Ausgabe, wodurch das Echo für jemanden, der mit make nicht vertraut ist, noch seltsamer erscheint.
Falstro
46
Zwei Nits: 1. Die Befehle sollten wirklich verbunden werden &&, denn ;wenn das Verzeichnis nicht existiert und das cdfehlschlägt, führt die Shell den Rest der Befehle im aktuellen Verzeichnis weiter aus, was zu mysteriösen „Datei nicht“ führen kann gefundene Nachrichten für Kompilierungen, Endlosschleifen beim Aufrufen von make oder Katastrophen für Regeln wie clean:: cd dir; rm -rf *. 2. Rufen Sie beim Aufrufen von Untermarken $(MAKE)stattdessen auf, makedamit die Optionen korrekt weitergegeben werden .
Andrewdotn
2
@perreal, ich definiere normalerweise eine Musterregel wie folgt: %-recursive:mit dem Körper: @T="$@";$(MAKE) -C some_dir $${T%-*}(Ich habe normalerweise auch eine for-Schleife, um eine Liste von Unterverzeichnissen zu durchlaufen, das $${T%-*}ist eine Bash-Erweiterung, die den -recursiveTeil des Zielnamens entfernt ) und dann definiert explizite Kurz Hand (und .PHONY) Ziele für jede, wie all: all-recursive, check: check-recursive, clean: clean-recursive.
Falstro
1
@ChristianStewart, wahr, wie in Kommentar 2 und 3 erwähnt.
Falstro
94
Ab GNU make 3.82 (Juli 2010) können Sie mit dem .ONESHELLspeziellen Ziel alle Rezeptzeilen in einer einzigen Instanziierung der Shell ausführen (fett hervorgehobene Mine):
Neues spezielles Ziel: .ONESHELLAnweisungen machen , eine einzelne Instanz der Shell aufzurufen und ihr das gesamte Rezept bereitzustellen , unabhängig davon, wie viele Zeilen sie enthält. [...]
.ONESHELL: # Only applies to all target
all:
cd ~/some_dir
pwd # Prints ~/some_dir if cd succeeded
Es genügt bemerken , dass pwdsich, wie auch funktioniert `pwd`(mit Backticks), aber $(shell pwd)und $(PWD)wird noch das Verzeichnis zurück , bevor das tun cdBefehl, so dass Sie sie nicht direkt nutzen können.
Anol
3
Ja, da die Variablen- und Funktionserweiterung vor der Ausführung der Befehle durch make erfolgt, während pwdund `pwd`von der Shell selbst ausgeführt wird.
Chnossos
2
Nicht jeder arbeitet an Legacy-Makefiles, und selbst dann geht es bei dieser Antwort darum, zu wissen, dass diese Möglichkeit besteht.
Chnossos
1
Dies kann eine ärgerliche / gefährliche Option sein, da nur der letzte Befehl für ein Ziel einen Fehler verursachen kann (frühere Befehlsfehler werden ignoriert), und wahrscheinlich wird dies niemand erwarten, der mit Ihnen zusammenarbeitet.
Tobii
1
Setzen Sie SHELLFLAGSauf -e -cund die Schale auf den ersten Befehl verlassen wird , die fehlschlägt.
Timothy Baldwin
21
Hier ist ein süßer Trick, um mit Verzeichnissen umzugehen und sie zu erstellen. Anstatt mehrzeilige Zeichenfolgen oder "cd" zu verwenden; Definieren Sie für jeden Befehl eine einfache chdir-Funktion wie folgt:
CHDIR_SHELL := $(SHELL)
define chdir
$(eval _D=$(firstword $(1) $(@D)))
$(info $(MAKE): cd $(_D)) $(eval SHELL = cd $(_D); $(CHDIR_SHELL))
endef
Dann müssen Sie es in Ihrer Regel nur noch so nennen:
all:
$(call chdir,some_dir)
echo "I'm now always in some_dir"
gcc -Wall -o myTest myTest.c
Sie können sogar Folgendes tun:
some_dir/myTest:
$(call chdir)
echo "I'm now always in some_dir"
gcc -Wall -o myTest myTest.c
"Niedlich"? Eher genug Seil, um sich in den Fuß zu schießen.
Tripleee
1
Ist dieses aktuelle Verzeichnis dann für die Befehle nur in dieser Regel oder für alle nachfolgend ausgeführten Regeln festgelegt? Funktioniert eine Variation davon auch unter Windows?
user117529
8
Dies ist natürlich Pausen für die parallele Ausführung ( -jn), das der ganze Sinn ist make wirklich.
Bobbogo
5
Das ist ein schlechter Hack. Wenn Sie jemals auf so etwas zurückgreifen müssen, verwenden Sie Makefiles nicht für das, wofür sie sind.
Gatopeich
4
Ich werde nicht widersprechen, dass es ein schlechter Hack ist, das ist sicher. Aber zeigt einige der bösen Dinge, die Sie tun können.
JoeS
9
Was soll es tun, wenn es dort ankommt? Jeder Befehl wird in einer Unterschale ausgeführt, sodass die Unterschale das Verzeichnis wechselt. Das Endergebnis ist jedoch, dass sich der nächste Befehl noch im aktuellen Verzeichnis befindet.
target:
$(shell cd ....); \
# ... commands execution in this directory
# ... no need to go back (using "cd -" or so)
# ... next target will be automatically in prev dir
Nein, das ist falsch. Insbesondere wird das $(shell cd ....)ausgeführt, wenn das Makefile zum ersten Mal analysiert wird, nicht, wenn dieses bestimmte Rezept ausgeführt wird.
Tripleee
@triplee Nicht ganz - das $(shell)wird nur erweitert, wenn make beschließt, ein Ziel zu erstellen . Wenn make das Rezept nie benötigt, wird es nicht erweitert.
make
wollte ich das Verzeichnis nie so ändern. Vielleicht sollten Sie einen anderen Ansatz für Ihre Lösung ausprobieren?cd dir; cmd file
kann fast immer sinnvoller ausgedrückt werden alscmd dir/file
..
Berücksichtigung eines bestimmten Werts geschrieben. Es ist wahr, dass die meisten Tools so konzipiert sind, dass Sie Ihr pwd dafür nicht ändern müssen. Dies ist jedoch nicht immer der Fall, und ich halte es nicht für eine gute Idee, es als "Fehler" zu bezeichnen, um zu glauben, dass Ihr Verzeichnis wichtig sein könnte.Antworten:
Es führt den Befehl tatsächlich aus und ändert das Verzeichnis in
some_directory
. Dies wird jedoch in einer Unterprozess-Shell ausgeführt und wirkt sich weder auf make noch auf die Shell aus, von der aus Sie arbeiten.Wenn Sie mehr Aufgaben ausführen möchten
some_directory
, müssen Sie ein Semikolon hinzufügen und auch die anderen Befehle anhängen. Beachten Sie, dass Sie keine Zeilenumbrüche verwenden können, da diese von make als Ende der Regel interpretiert werden. Daher müssen alle Zeilenumbrüche, die Sie aus Gründen der Übersichtlichkeit verwenden, durch einen Backslash maskiert werden.Zum Beispiel:
Beachten Sie auch, dass das Semikolon zwischen jedem Befehl erforderlich ist, obwohl Sie einen Backslash und eine Newline hinzufügen. Dies liegt an der Tatsache, dass die gesamte Zeichenfolge von der Shell als einzelne Zeile analysiert wird. Wie in den Kommentaren erwähnt, sollten Sie '&&' verwenden, um Befehle zu verbinden. Dies bedeutet, dass sie nur ausgeführt werden, wenn der vorhergehende Befehl erfolgreich war.
Dies ist besonders wichtig, wenn Sie zerstörerische Arbeiten ausführen, z. B. Aufräumen, da Sie sonst das falsche Material zerstören, falls dies
cd
aus irgendeinem Grund fehlschlägt.Eine häufige Verwendung ist jedoch das Aufrufen von make im Unterverzeichnis, in das Sie möglicherweise schauen möchten. Hierfür gibt es eine Befehlszeilenoption, sodass Sie sich nicht selbst anrufen müssen
cd
, sodass Ihre Regel so aussehen würdedas wird sich in
some_dir
dasMakefile
dort mit dem Ziel "all" ändern und ausführen . Verwenden Sie als bewährte Methode,$(MAKE)
anstattmake
direkt aufzurufen , da Sie die richtige make-Instanz aufrufen müssen (wenn Sie beispielsweise eine spezielle make-Version für Ihre Build-Umgebung verwenden) und beim Ausführen ein etwas anderes Verhalten bereitstellen mit bestimmten Schaltern, wie z-t
.Machen Sie für den Datensatz immer den Befehl, den er ausführt (sofern er nicht ausdrücklich unterdrückt wird), auch wenn er keine Ausgabe hat, was Sie sehen.
quelle
&&
, denn;
wenn das Verzeichnis nicht existiert und dascd
fehlschlägt, führt die Shell den Rest der Befehle im aktuellen Verzeichnis weiter aus, was zu mysteriösen „Datei nicht“ führen kann gefundene Nachrichten für Kompilierungen, Endlosschleifen beim Aufrufen von make oder Katastrophen für Regeln wieclean:: cd dir; rm -rf *
. 2. Rufen Sie beim Aufrufen von Untermarken$(MAKE)
stattdessen auf,make
damit die Optionen korrekt weitergegeben werden .%-recursive:
mit dem Körper:@T="$@";$(MAKE) -C some_dir $${T%-*}
(Ich habe normalerweise auch eine for-Schleife, um eine Liste von Unterverzeichnissen zu durchlaufen, das$${T%-*}
ist eine Bash-Erweiterung, die den-recursive
Teil des Zielnamens entfernt ) und dann definiert explizite Kurz Hand (und .PHONY) Ziele für jede, wieall: all-recursive
,check: check-recursive
,clean: clean-recursive
.Ab GNU make 3.82 (Juli 2010) können Sie mit dem
.ONESHELL
speziellen Ziel alle Rezeptzeilen in einer einzigen Instanziierung der Shell ausführen (fett hervorgehobene Mine):quelle
pwd
sich, wie auch funktioniert`pwd`
(mit Backticks), aber$(shell pwd)
und$(PWD)
wird noch das Verzeichnis zurück , bevor das tuncd
Befehl, so dass Sie sie nicht direkt nutzen können.pwd
und`pwd`
von der Shell selbst ausgeführt wird.SHELLFLAGS
auf-e -c
und die Schale auf den ersten Befehl verlassen wird , die fehlschlägt.Hier ist ein süßer Trick, um mit Verzeichnissen umzugehen und sie zu erstellen. Anstatt mehrzeilige Zeichenfolgen oder "cd" zu verwenden; Definieren Sie für jeden Befehl eine einfache chdir-Funktion wie folgt:
Dann müssen Sie es in Ihrer Regel nur noch so nennen:
Sie können sogar Folgendes tun:
quelle
-jn
), das der ganze Sinn ist make wirklich.Was soll es tun, wenn es dort ankommt? Jeder Befehl wird in einer Unterschale ausgeführt, sodass die Unterschale das Verzeichnis wechselt. Das Endergebnis ist jedoch, dass sich der nächste Befehl noch im aktuellen Verzeichnis befindet.
Mit GNU make können Sie Folgendes tun:
quelle
$(shell ...)
Wanncd $(BIN); ls
odercd $(BIN) && ls
(wie @andrewdotn betonte) ausreichen würde.Dir ändern
quelle
So was:
Viel Glück!
quelle
$(shell cd ....)
ausgeführt, wenn das Makefile zum ersten Mal analysiert wird, nicht, wenn dieses bestimmte Rezept ausgeführt wird.$(shell)
wird nur erweitert, wenn make beschließt, ein Ziel zu erstellen . Wenn make das Rezept nie benötigt, wird es nicht erweitert.