Wie überprüfe ich, ob eine Datei in Makefile vorhanden ist, damit ich sie löschen kann?

135

Im sauberen Bereich von Makefileversuche ich zu überprüfen, ob die Datei vorhanden ist, bevor ich sie dauerhaft lösche. Ich benutze diesen Code, erhalte aber Fehler.

Was stimmt damit nicht?

 if [ -a myApp ]
 then
     rm myApp
 fi

Ich erhalte diese Fehlermeldung

 if [ -a myApp ]
 /bin/sh: Syntax error: end of file unexpected (expecting "then")
 make: *** [clean] Error 2
Abruzzen Forte e Gentile
quelle
Ist myApp eine Variable oder ein tatsächlicher Dateiname?
BugFinder
myApp ist für myApplication, dh den Dateinamen des Erstellungsprozesses.
Abruzzen Forte e Gentile
5
Wenn Sie nur vermeiden möchten, dass die Datei angehalten wird, wenn sie nicht vorhanden ist, rm -rf myAppkönnte dies eine Alternative sein. Oder stellen Sie dem Befehl einen Bindestrich ( -rm myApp) voran , damit make den Fehler von rm ignoriert (es wird jedoch eine hässliche Nachricht ausgegeben).
Thomasa88
1
Ihr Problem war, dass make jede Zeile in einer Regel als separaten Befehl behandelt und sie einzeln an die Shell sendet. Es ist, als würde man nur "if [-a myApp]" alleine ausführen. Wenn Sie diesen Fehler erhalten, benötigen Sie entweder eine Lösung, die die Zeilen zu einer zusammenfügt (using) oder die dazu führt, dass jede Zeile unabhängig von der anderen ist. Es gibt jetzt einige davon unten.
Michael

Antworten:

40

In der zweiten Top-Antwort ifeqwird jedoch nicht erwähnt, dass diese auf derselben Ebene wie der Name des Ziels liegen müssen. Um beispielsweise eine Datei nur dann herunterzuladen, wenn sie derzeit nicht vorhanden ist, kann der folgende Code verwendet werden:

download:
ifeq (,$(wildcard ./glob.c))
    curl … -o glob.c
endif

# THIS DOES NOT WORK!
download:
    ifeq (,$(wildcard ./glob.c))
        curl … -o glob.c
    endif
cnst
quelle
hat nicht funktioniert, bis ich Backslash `` nach if fi
Taras Matsyk
3
Diese Antwort wird etwas seltsam sein; Die Dateiprüfung erfolgt, wenn das Makefile verarbeitet wird. Die Aktion wird jedoch später ausgeführt, wenn das Ziel von make erstellt wird. Wenn Sie die Datei in der Zwischenzeit löschen, wird die Datei nicht erstellt. Ich habe eine Bearbeitung vorgenommen, um es klarer zu machen.
Michael
Vielen Dank! Dieser Punkt war beim Lesen des Handbuchs nicht klar.
Kevin Buchs
207

Es ist seltsam zu sehen, dass so viele Leute dafür Shell-Scripting verwenden. Ich habe nach einer Möglichkeit gesucht, die native Makefile-Syntax zu verwenden, da ich diese außerhalb eines Ziels schreibe. Mit der wildcardFunktion können Sie überprüfen, ob eine Datei vorhanden ist:

 ifeq ($(UNAME),Darwin)
     SHELL := /opt/local/bin/bash
     OS_X  := true
 else ifneq (,$(wildcard /etc/redhat-release))
     OS_RHEL := true
 else
     OS_DEB  := true
     SHELL := /bin/bash
 endif 

Aktualisieren:

Ich habe einen Weg gefunden, der wirklich für mich funktioniert:

ifneq ("$(wildcard $(PATH_TO_FILE))","")
    FILE_EXISTS = 1
else
    FILE_EXISTS = 0
endif
holms
quelle
4
versuchte dies, aber ich Makefile:133: *** unterminated call to function `wildcard': missing `)'. Stop.
bekomme
1
Hervorragende Verwendung von Platzhaltern, sodass dies mit dem Makefile selbst möglich ist.
Ordentlich
3
Hilft auch zu verstehen, was $(wildcard pattern)tatsächlich tut. Siehe Link .
Dr. Dan
1
FILE_EXISTS := $(or $(and $(wildcard $(PATH_TO_FILE)),1),0)
Prägnanter
2
Wenn Sie unter Cygwin unter Windows ausgeführt werden, ist es erwähnenswert, dass bei Verwendung eines Platzhalters auf diese Weise der Dateiname zwischen Groß- und Kleinschreibung unterschieden wird. Wenn die Datei also vorhanden ist, die Groß- und Kleinschreibung jedoch nicht dem Pfad entspricht, wird sie nicht gefunden. Es scheint keine Möglichkeit zu geben, dieses Verhalten innerhalb des Makefiles zu überschreiben.
Roger Sanders
59

Das Problem ist, wenn Sie Ihren Befehl auf mehrere Zeilen aufteilen. Sie können also entweder das \am Zeilenende für die Fortsetzung wie oben verwenden oder alles in einer Zeile mit dem &&Operator in bash abrufen.

Anschließend können Sie mit einem testBefehl testen, ob die Datei vorhanden ist, z.

test -f myApp && echo File does exist

-f file True, wenn eine Datei vorhanden ist und eine reguläre Datei ist.

-s file True, wenn die Datei vorhanden ist und eine Größe größer als Null hat.

oder nicht:

test -f myApp || echo File does not exist
test ! -f myApp && echo File does not exist

Das testentspricht dem [Befehl.

[ -f myApp ] && rm myApp   # remove myApp if it exists

und es würde wie in Ihrem ursprünglichen Beispiel funktionieren.

Siehe: help [oder help testfür weitere Syntax.

Kenorb
quelle
4
Ich hätte gestimmt, aber Sie haben nicht gewarnt, dass dies -sein Sonderfall für ist exists and has a size greater than zero. Die geschriebene Frage ist größenunabhängig, daher sollte die Existenz anhand test -eeiner Datei oder -deines Verzeichnisses überprüft werden . Leere Dateien können besonders nützlich sein (aus Mangel an einem besseren Begriff) Indikatoren / Sentinels, die für sehr relevant sein können make.
underscore_d
Danke für den Vorschlag. -fStandardmäßig geändert , da es häufiger verwendet wird.
Kenorb
Wie komme ich testunter Windows?
Thowa
3
Damit dies funktioniert, müssen Sie || trueam Ende hinzufügen, damit der Befehl true zurückgibt, wenn die Datei nicht vorhanden ist.
jcubic
2
@ AndrewMackenzie test -f myApp || CMD, beachten Sie die ||, also wenn -ffehlschlägt - nicht existiert ( ||), dann führen Sie den Befehl aus. Macht das Sinn?
Kenorb
50

Für die Fortsetzung ist möglicherweise ein Backslash am Ende der Zeile erforderlich (obwohl dies möglicherweise von der Version von make abhängt):

if [ -a myApp ] ; \
then \
     rm myApp ; \
fi;
Mark Wilkins
quelle
2
scheint keine Makefile-Syntax zu sein? sunsite.ualberta.ca/Documentation/Gnu/make-3.79/html_chapter/…
holms
@holms ist eine Bash-Syntax. Durch das Verlassen der neuen Zeilen kann es als einzelner Bash-Befehl behandelt werden. Standardmäßig wäre in makeeiner neuen Zeile ein neuer Bash-Befehl. Die größte Einschränkung davon, abgesehen von dem Ärger, viele \ am Ende der Zeilen zu haben, ist, dass jeder Befehl mit dem beendet werden muss, ;was sonst in bash impliziert wäre.
Flungo
1
Die richtige Antwort ist von @holms. Weder '\' noch ein Platzhalter sind genau für diesen Zweck vorgesehen. Der Grund, warum Sie Platzhalter verwenden würden, ist die Klarheit des Codes. Diese Methode ist nicht nur weniger lesbar, sondern auch anfälliger für Syntaxfehler.
Maitreya
1
Der bereitgestellte Link @holms funktioniert nicht mehr. Verwenden Sie stattdessen gnu.org/software/make/manual/make.html#Conditionals
fero
Dies ist eine großartige Antwort, da sie mit dem, was der ursprüngliche Fragesteller getan hat, übereinstimmt und davon abhängt, ob die Datei zum Zeitpunkt der Erstellung des Ziels vorhanden ist und nicht zum Zeitpunkt des Starts des Makefiles, was die meisten Leute erwarten würden und wollen die meiste Zeit. In einigen seltsamen Fällen wäre die Antwort von @cnst besser.
Michael
30

Oder setzen Sie es einfach in eine Zeile, makewie es Ihnen gefällt:

if [ -a myApp ]; then rm myApp; fi;
Jeroen
quelle
Das ist eine großartige Antwort in diesem Fall (und sollte Upvotes erhalten), funktioniert aber nicht so gut, wenn die Aktionen komplexer wären. In diesem Fall wechseln Sie einfach zu \
Michael
[-a myApp] && rm myApp
Robin Hsu
14

Ein Semikolon fehlt

if [ -a myApp ];
then
  rm myApp
fi

Ich gehe jedoch davon aus, dass Sie vor dem Löschen auf Existenz prüfen, um eine Fehlermeldung zu vermeiden. Wenn ja, können Sie einfach verwenden, rm -f myAppwelche "erzwingt" das Löschen, dh kein Fehler, wenn die Datei nicht vorhanden ist.

Drysdam
quelle
1
Das ist genau das, was ich tun wollte. Vielen Dank.
Abruzzen Forte e Gentile
1
Dies funktioniert in einem Makefile nicht, da das if immer noch über mehrere Zeilen verteilt ist. Sie müssen dies entweder in eine Zeile einfügen oder \ es verwenden. und selbst wenn Sie die \ es hinzugefügt haben, fehlen Ihnen noch einige Semikolons.
Michael
13
FILE1 = /usr/bin/perl
FILE2 = /nofile

ifeq ($(shell test -e $(FILE1) && echo -n yes),yes)
    RESULT1=$(FILE1) exists.
else
    RESULT1=$(FILE1) does not exist.
endif

ifeq ($(shell test -e $(FILE2) && echo -n yes),yes)
    RESULT2=$(FILE2) exists.
else
    RESULT2=$(FILE2) does not exist.
endif

all:
    @echo $(RESULT1)
    @echo $(RESULT2)

Ausführungsergebnisse:

bash> make
/usr/bin/perl exists.
/nofile does not exist.
Robin Hsu
quelle
Das hat mir geholfen, ich glaube einige haben verpasst, dass das OP nach Makefile gefragt hat. Ich habe nicht verstanden, warum "&& echo -n yes" notwendig ist. Erläuterung: Wenn test -e 1 zurückgibt (nicht gefunden), führt die Shell den Echo-Befehl nicht aus und stimmt daher nicht mit dem Ja in ifeq überein
Brad Dre
Ich denke, Sie verstehen es richtig in Ihrer Erklärung.
Robin Hsu
@BradDre - Ändert echo -n yesden Erfolg testder Zeichenfolge yesohne NL. Der ifeqkann es dann mit dem fest codierten vergleichen yes. Alles nur, weil ifeqeine Zeichenfolge verglichen werden soll, nicht der Erfolgsstatus eines Shell-Befehls.
Jesse Chisholm
8

Einzeilige Lösung:

   [ -f ./myfile ] && echo exists

Einzeilige Lösung mit Fehleraktion:

   [ -f ./myfile ] && echo exists || echo not exists

Beispiel in meinen make cleanAnweisungen verwendet:

clean:
    @[ -f ./myfile ] && rm myfile || true

Und make cleanfunktioniert immer ohne Fehlermeldungen!

Antonio Feitosa
quelle
3
mach einfach @rm -f myfile. Aufgrund des Flags "-f" wird "rm" mit 0 beendet, unabhängig davon, ob die Datei vorhanden ist oder nicht.
Alexey Polonsky
Oder -rm myfileder Lead Dash sagt, dass Fehler ignoriert werden sollen.
Jesse Chisholm
1
In meinem Fall das || weglassen <Fehleraktion> verursachte Probleme. Ihr letztes Beispiel, in dem Sie true zurückgegeben haben, wenn die Datei nicht vorhanden war, hat dies gut angesprochen. Danke dir!
Flic
Für mich muss der Weg zu myfile mit Apostroph (') sein:@[ -f 'myfile' ] && rm myfile
Sten
0
test ! -f README.md || echo 'Support OpenSource!' >> README.md

"Wenn README.md nicht vorhanden ist, tun Sie nichts (und beenden Sie es erfolgreich). Andernfalls fügen Sie Text an das Ende an."

Wenn Sie &&stattdessen verwenden ||, wird ein Fehler generiert, wenn die Datei nicht vorhanden ist:

Makefile:42: recipe for target 'dostuff' failed
make: *** [dostuff] Error 1
Matt Janssen
quelle
0

Ich habe es versucht:

[ -f $(PROGRAM) ] && cp -f $(PROGRAM) $(INSTALLDIR)

Und der positive Fall hat funktioniert, aber meine Ubuntu-Bash-Shell nennt dies WAHR und bricht auf der Kopie ab:

[ -f  ] && cp -f  /home/user/proto/../bin/
cp: missing destination file operand after '/home/user/proto/../bin/'

Nachdem ich diesen Fehler erhalten habe, google ich, wie ich überprüfe, ob eine Datei in make vorhanden ist, und dies ist die Antwort ...

Scott Milano
quelle
0
ifneq ("$(wildcard $(PATH_TO_FILE))","")
    FILE_EXISTS = 1
else
    FILE_EXISTS = 0
endif

Diese oben beschriebene Lösung funktioniert am besten. Stellen Sie jedoch sicher, dass Sie die Zuweisung PATH_TO_FILE nicht stringifizieren.

PATH_TO_FILE = "/usr/local/lib/libhl++.a" # WILL NOT WORK

Es muss sein

PATH_TO_FILE = /usr/local/lib/libhl++.a
Om Narasimhan
quelle
-2

Die Antworten wie die von @ mark-wilkins mit \, um die Zeilen fortzusetzen und; Sie in der Shell zu beenden oder wie die von @kenorb, dies in eine Zeile zu ändern, ist gut und wird dieses Problem beheben.

Es gibt eine einfachere Antwort auf das ursprüngliche Problem (wie @ alexey-polonsky hervorhob). Verwenden Sie das Flag -f für rm, damit kein Fehler ausgelöst wird

rm -f myApp

Dies ist einfacher, schneller und zuverlässiger. Achten Sie nur darauf, dass Sie keinen Schrägstrich und keine leere Variable erhalten

rm -f / $ (myAppPath) # TUN SIE DAS NIE

Möglicherweise löschen Sie Ihr System.

Michael
quelle
1
Dies ist eine gute Antwort zum Löschen der Datei, die das OP hatte, aber ich bin mir ziemlich sicher, dass die meisten Leute, die diese Frage finden, nicht wirklich nach dem Löschen von Dateien suchen. Dies wird tatsächlich durch die Tatsache belegt, dass die meisten Antworten überhaupt nicht erwähnen rm; Übrigens bin ich mir ziemlich sicher, dass rm -f /$(myAppPath)das auch keinen Schaden anrichtet, da /es sich um ein Verzeichnis handelt und das -rfehlt.
cnst
Dies ist keine einfachere Lösung. Wie @cnst sagte, kann es Gründe geben, warum das Originalplakat nicht einfach eine machen möchte rm -f, z. B. rmeine Variable.
Dan Nguyen