Come posso verificare se il file esiste in Makefile in modo da poterlo eliminare?


135

Nella sezione pulita del mio Makefilesto cercando di verificare se il file esiste prima di eliminare definitivamente. Uso questo codice ma ricevo errori.

Che cosa c'è che non va?

 if [ -a myApp ]
 then
     rm myApp
 fi

Ricevo questo messaggio di errore

 if [ -a myApp ]
 /bin/sh: Syntax error: end of file unexpected (expecting "then")
 make: *** [clean] Error 2

MyApp è una variabile o un nome file effettivo?
BugFinder,

myApp è per myApplication, ovvero il nome file dal processo di compilazione.
Abruzzo Forte e Gentile,

5
Se vuoi solo evitare di fermarti se il file non esiste, rm -rf myApppotrebbe essere un'alternativa. O precedendo il comando con un trattino ( -rm myApp) per fare ignorare l'errore da rm (stamperà comunque un brutto messaggio).
thomasa88,

1
Il tuo problema era che considera ogni riga di una regola come un comando separato e le invia individualmente alla shell. È come eseguire solo `if [-a myApp] 'da solo. Se ricevi questo errore, hai bisogno di una soluzione che unisce le linee in una (usando) o che finisce con ciascuna linea indipendentemente dall'altra. Ora ce ne sono molti qui sotto.
Michael,

Risposte:


40

La seconda risposta principale menziona ifeq, tuttavia, non menziona il fatto che devono trovarsi allo stesso livello del nome della destinazione, ad esempio per scaricare un file solo se non esiste attualmente, è possibile utilizzare il seguente codice:

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

# THIS DOES NOT WORK!
download:
    ifeq (,$(wildcard ./glob.c))
        curl … -o glob.c
    endif

non ha funzionato fino a quando non ho aggiunto la barra rovesciata `` after if fi
Taras Matsyk il

3
Questa risposta sarà un po 'strana; il controllo del file avviene quando il makefile viene elaborato ma l'azione avverrà in seguito quando il target viene creato da make. Se si elimina il file nel frattempo, il file non verrà creato. Ho apportato una modifica per renderlo più chiaro.
Michael,

Grazie mille! Questo punto non era chiaro dalla lettura del manuale.
Kevin Buchs

207

È strano vedere così tante persone che usano gli script di shell per questo. Stavo cercando un modo per usare la sintassi del makefile nativo, perché sto scrivendo questo al di fuori di qualsiasi target. È possibile utilizzare la wildcardfunzione per verificare l'esistenza del file:

 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 

Aggiornare:

Ho trovato un modo che funziona davvero per me:

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

4
provato questo, ma continuo a ricevereMakefile:133: *** unterminated call to function `wildcard': missing `)'. Stop.
Ant6n

1
Ottimo uso dei caratteri jolly, quindi può essere fatto con il makefile stesso. Neat :)
anoop

3
Aiuta a capire anche cosa $(wildcard pattern)fa realmente. Vedi link .
Dr. Dan,

1
Più conciso:FILE_EXISTS := $(or $(and $(wildcard $(PATH_TO_FILE)),1),0)
cmaster - reintegrare monica il

2
Vale la pena notare che se si esegue Windows con Cygwin, l'utilizzo di caratteri jolly in questo modo corrisponde a una distinzione tra maiuscole e minuscole sul nome file, quindi se il file esiste ma con un caso diverso dal percorso non verrà trovato. Non sembra esserci alcun modo per ignorare questo comportamento all'interno del makefile.
Roger Sanders,

59

Il problema è quando si divide il comando su più righe. Quindi, puoi usare o \alla fine delle righe per la continuazione come sopra oppure puoi ottenere tutto su una riga con l' &&operatore in bash.

Quindi è possibile utilizzare un testcomando per verificare se il file esiste, ad esempio:

test -f myApp && echo File does exist

-f file Vero se il file esiste ed è un file normale.

-s file Vero se il file esiste e ha una dimensione maggiore di zero.

o no:

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

Il testequivale a [comando.

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

e funzionerebbe come nel tuo esempio originale.

Vedi: help [o help testper ulteriore sintassi.


4
Avrei votato a favore, ma non hai avvertito che -sè un caso speciale per exists and has a size greater than zero. La domanda scritta è indipendente dalla dimensione, quindi l'esistenza dovrebbe essere verificata usando test -eper un file o -dper una directory. I file vuoti possono essere particolarmente utili come indicatori / sentinelle (per mancanza di un termine migliore), che potrebbero essere abbastanza rilevanti per make.
underscore_d

Grazie per il suggerimento Modificato -fper impostazione predefinita, poiché è più comune da usare.
Kenorb,

Come posso accedere testa Windows?
thowa,

3
Affinché ciò funzioni, è necessario aggiungere || truealla fine in modo che il comando ritorni vero quando il file non esiste.
jcubic,

2
@AndrewMackenzie test -f myApp || CMD, nota il ||, quindi se -ffallirà - non esiste ( ||), quindi esegui il comando. Ha senso?
Kenorb,

50

Potrebbe essere necessaria una barra rovesciata alla fine della riga per la continuazione (anche se forse ciò dipende dalla versione di make):

if [ -a myApp ] ; \
then \
     rm myApp ; \
fi;

2
sembra non essere una sintassi di Makefile? sunsite.ualberta.ca/Documentation/Gnu/make-3.79/html_chapter/…
holms

@holms è una sintassi bash. Sfuggendo alle nuove linee, consente di gestirlo come un singolo comando bash. Di default in makeuna nuova riga sarebbe un nuovo comando bash. Il principale avvertimento di questo, oltre al fastidio di avere un sacco di \ alla fine delle linee è che ogni comando deve essere terminato con il ;quale altrimenti sarebbe implicito in bash.
flungo,

1
La risposta giusta è di @holms. Né '\' né jolly sono esattamente destinati a questo scopo. Il motivo per cui dovresti usare i caratteri jolly è per la chiarezza del codice. Questo metodo non solo è meno leggibile, ma è anche più soggetto a errori di sintassi.
Maitreya,

1
il link @holms fornito non funziona più, usa invece gnu.org/software/make/manual/make.html#Conditionals
fero

questa è un'ottima risposta perché corrisponde a ciò che è sbagliato in ciò che ha fatto l'interrogatore originale e funzionerà a seconda che il file esista al momento della creazione della destinazione piuttosto che al momento dell'avvio del Makefile, che è ciò che la maggior parte delle persone si aspetterebbe e voglio il più delle volte. In alcuni casi strani la risposta di @cnst sarebbe migliore.
Michael,

31

O semplicemente mettilo su una riga, come makepiace:

if [ -a myApp ]; then rm myApp; fi;

è un'ottima risposta in questo caso (e dovrebbe ottenere voti positivi) ma non funzionerà così bene se le azioni fossero più complesse, nel qual caso basta passare a usare \
Michael

[-a myApp] && rm myApp
Robin Hsu,

14

Manca un punto e virgola

if [ -a myApp ];
then
  rm myApp
fi

Tuttavia, suppongo che tu stia verificando l'esistenza prima dell'eliminazione per evitare un messaggio di errore. Se è così, puoi semplicemente usare rm -f myAppquali "forzano" l'eliminazione, cioè non si sbaglia se il file non esiste.


1
Questo è esattamente quello che volevo fare. Molte grazie.
Abruzzo Forte e Gentile,

1
questo non funzionerà in un Makefile perché l'if è ancora distribuito su più righe - devi metterlo su una riga o usare \ es. e anche se hai aggiunto i \ ti mancano ancora alcuni punti e virgola.
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)

risultati di esecuzione:

bash> make
/usr/bin/perl exists.
/nofile does not exist.

Questo mi ha aiutato, penso che alcuni abbiano perso il fatto che l'OP chiedesse del makefile. Non ho capito perché "&& echo -n yes" è necessario. Spiegazione: se test -e restituisce 1 (non trovato), la shell non eseguirà il comando echo e quindi non corrisponderà al yes in ifeq
Brad Dre,

Penso che tu lo capisca correttamente nella tua spiegazione.
Robin Hsu,

@BradDre - echo -n yesCambia il successo ditest stringa yessenza NL. La ifeqsi può quindi confrontare con l'hard coded yes. Tutto perché ifeqvuole confrontare una stringa, non uno stato di successo da un comando shell.
Jesse Chisholm,

8

Una soluzione di linea:

   [ -f ./myfile ] && echo exists

Soluzione a una riga con azione di errore:

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

Esempio usato nelle mie make cleandirettive:

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

E make cleanfunziona sempre senza messaggi di errore!


3
basta fare @rm -f myfile. A causa del flag "-f", "rm" uscirà con 0 indipendentemente dal fatto che il file esista o meno.
Alexey Polonsky,

Oppure, -rm myfileil trattino iniziale che indica di ignorare qualsiasi errore.
Jesse Chisholm,

1
Nel mio caso, tralasciando la || <azione di errore> ha causato problemi. Il tuo esempio finale, in cui hai restituito true se il file non esisteva, è stato risolto correttamente. Grazie!
Flic

Per me il percorso verso myfile deve essere con apostrofo ('):@[ -f 'myfile' ] && rm myfile
Sten

0
test ! -f README.md || echo 'Support OpenSource!' >> README.md

"Se README.md non esiste, non eseguire alcuna operazione (e uscire correttamente). In caso contrario, aggiungere il testo alla fine."

Se usi &&invece di ||generare un errore quando il file non esiste:

Makefile:42: recipe for target 'dostuff' failed
make: *** [dostuff] Error 1

0

Stavo provando:

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

E il caso positivo ha funzionato ma la mia shell ubh bash chiama questo VERO e si rompe sulla copia:

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

Dopo aver ricevuto questo errore, google come controllare se esiste un file in make, e questa è la risposta ...


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

Questa soluzione pubblicata sopra funziona meglio. Ma assicurati di non stringere il compito PATH_TO_FILE ad es.

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

Dev'essere

PATH_TO_FILE = /usr/local/lib/libhl++.a

-2

Le risposte come quella di @ mark-wilkins usano \ per continuare le linee e; terminarli nella shell o come quelli di @kenorb cambiando questo in una riga sono buoni e risolveranno questo problema.

c'è una risposta più semplice al problema originale (come ha sottolineato @ alexey-polonsky). Utilizzare il flag -f per rm in modo che non si inneschi un errore

rm -f myApp

questo è più semplice, più veloce e più affidabile. Fai solo attenzione a non finire con una barra e una variabile vuota

rm -f / $ (myAppPath) #NON FARE MAI QUESTO

potresti finire per eliminare il tuo sistema.


1
Questa è una buona risposta per cancellare il file che aveva l'OP, ma sono abbastanza sicuro che la maggior parte delle persone che trovano questa domanda non stanno effettivamente cercando di eliminare alcun file; questo è in realtà dimostrato dal fatto che la maggior parte delle risposte non menziona nemmeno rmaffatto; A proposito, sono abbastanza sicuro che rm -f /$(myAppPath)non farà alcun danno, perché /è una directory e -rmanca.
primo

Questa non è una soluzione più semplice. Come ha detto @cnst, potrebbero esserci dei motivi per cui il poster originale non vuole semplicemente fare un rm -fesempio, ad esempio potrebbe voler rmuna variabile.
Dan Nguyen,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.