Comment vérifier si le fichier existe dans Makefile pour pouvoir le supprimer?

135

Dans la section propre de mon Makefile, j'essaie de vérifier si le fichier existe avant de le supprimer définitivement. J'utilise ce code mais je reçois des erreurs.

Qu'est ce qui ne va pas avec ça?

 if [ -a myApp ]
 then
     rm myApp
 fi

Je reçois ce message d'erreur

 if [ -a myApp ]
 /bin/sh: Syntax error: end of file unexpected (expecting "then")
 make: *** [clean] Error 2
Abruzzo Forte e Gentile
la source
MyApp est-il une variable ou un nom de fichier réel?
BugFinder
myApp est pour myApplication, c'est-à-dire le nom de fichier par le processus de construction.
Abruzzo Forte e Gentile
5
Si vous voulez simplement éviter d'arrêter si le fichier n'existe pas, cela rm -rf myApppourrait être une alternative. Ou précéder la commande d'un tiret ( -rm myApp) pour faire ignorer l'erreur de rm (il affichera cependant un message moche).
thomasa88
1
Votre problème était que make traite chaque ligne d'une règle comme une commande distincte et les envoie individuellement au shell. C'est comme exécuter «if [-a myApp]» seul. Si vous obtenez cette erreur, vous avez besoin d'une solution qui joint les lignes en une seule (en utilisant) ou qui se termine par chaque ligne indépendante de l'autre. Il y en a maintenant plusieurs ci-dessous.
Michael

Réponses:

40

La deuxième réponse en haut mentionne ifeq, cependant, elle ne mentionne pas que ceux-ci doivent être au même niveau que le nom de la cible, par exemple, pour télécharger un fichier uniquement s'il n'existe pas actuellement, le code suivant pourrait être utilisé:

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
la source
n'a pas fonctionné jusqu'à ce que j'aie ajouté une barre oblique inverse `` après si fi
Taras Matsyk
3
Cette réponse sera un peu bizarre; la vérification du fichier se produit lorsque le makefile est traité mais l'action se produira plus tard lorsque la cible sera construite par make. Si vous supprimez le fichier entre-temps, le fichier ne sera pas créé. J'ai mis un montage pour le rendre plus clair.
Michael le
Merci beaucoup! Ce point n'était pas clair à la lecture du manuel.
Kevin Buchs
207

C'est étrange de voir autant de gens utiliser des scripts shell pour cela. Je cherchais un moyen d'utiliser la syntaxe native makefile, car j'écris ceci en dehors de toute cible. Vous pouvez utiliser la wildcardfonction pour vérifier si le fichier existe:

 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 

Mettre à jour:

J'ai trouvé un moyen qui fonctionne vraiment pour moi:

ifneq ("$(wildcard $(PATH_TO_FILE))","")
    FILE_EXISTS = 1
else
    FILE_EXISTS = 0
endif
holms
la source
4
essayé ceci, mais je continue juste à obtenirMakefile:133: *** unterminated call to function `wildcard': missing `)'. Stop.
Ant6n
1
Grande utilisation du caractère générique, donc cela peut être fait avec makefile lui-même. Neat :)
anoop
3
Aide à comprendre également ce que $(wildcard pattern)fait réellement. Voir le lien .
Dr Dan
1
Plus concis:FILE_EXISTS := $(or $(and $(wildcard $(PATH_TO_FILE)),1),0)
cmaster - réintégrer monica
2
Il est intéressant de noter que si vous exécutez sous Windows sous cygwin, l'utilisation du caractère générique de cette manière fait une correspondance sensible à la casse sur le nom de fichier, donc si le fichier existe mais avec une casse différente du chemin, il ne sera pas trouvé. Il ne semble pas y avoir de moyen de remplacer ce comportement dans le makefile.
Roger Sanders
59

Le problème est lorsque vous divisez votre commande sur plusieurs lignes. Ainsi, vous pouvez soit utiliser la \fin des lignes pour la suite comme ci-dessus, soit tout obtenir sur une seule ligne avec l' &&opérateur dans bash.

Ensuite, vous pouvez utiliser une testcommande pour tester si le fichier existe, par exemple:

test -f myApp && echo File does exist

-f file Vrai si le fichier existe et est un fichier normal.

-s file Vrai si le fichier existe et a une taille supérieure à zéro.

ou ne:

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

Le testéquivaut à la [commande.

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

et cela fonctionnerait comme dans votre exemple original.

Voir: help [ou help testpour plus de syntaxe.

Kenorb
la source
4
J'aurais voté pour, mais vous n'avez pas prévenu qu'il -ss'agit d'un cas particulier exists and has a size greater than zero. La question telle qu'elle est écrite est indépendante de la taille, donc l'existence doit être vérifiée en utilisant test -epour un fichier ou -dpour un répertoire. Les fichiers vides peuvent être particulièrement utiles en tant qu'indicateurs / sentinelles (faute d'un meilleur terme), ce qui pourrait être tout à fait pertinent pour make.
underscore_d
Merci pour la suggestion. Modifié -fpar défaut, car il est plus courant à utiliser.
kenorb le
Comment puis-je accéder testà Windows?
thowa
3
Pour que cela fonctionne, vous devez ajouter || trueà la fin afin que la commande retourne true lorsque le fichier n'existe pas.
jcubic
2
@AndrewMackenzie test -f myApp || CMD, notez le ||, donc si -féchouera - n'existe pas ( ||), puis exécutez la commande. Est-ce que ça fait du sens?
kenorb
50

Il peut avoir besoin d'une barre oblique inverse à la fin de la ligne pour la suite (bien que cela dépende peut-être de la version de make):

if [ -a myApp ] ; \
then \
     rm myApp ; \
fi;
Mark Wilkins
la source
2
ne semble pas être une syntaxe Makefile? sunsite.ualberta.ca/Documentation/Gnu/make-3.79/html_chapter/…
holms
@holms est une syntaxe bash. En échappant aux nouvelles lignes, cela permet de le gérer comme une seule commande bash. Par défaut, makeune nouvelle ligne serait une nouvelle commande bash. La mise en garde principale de ceci, autre que le désagrément d'avoir beaucoup \ à la fin des lignes est que chaque commande doit se terminer par le ;qui serait autrement implicite dans bash.
flungo
1
La bonne réponse est par @holms. Ni «\» ni joker ne sont exactement destinés à cette fin. La raison pour laquelle vous utiliseriez un caractère générique est pour la clarté du code. Non seulement cette méthode est moins lisible, mais elle est plus sujette aux erreurs de syntaxe.
Maitreya
1
le lien @holms fourni ne fonctionne plus, utilisez plutôt gnu.org/software/make/manual/make.html#Conditionals
fero
c'est une excellente réponse car elle correspond à ce qui ne va pas avec ce que le questionneur d'origine a fait et cela fonctionnera selon que le fichier existe au moment de la construction de la cible plutôt qu'au moment du démarrage du Makefile, ce à quoi la plupart des gens s'attendent et veulent la plupart du temps. Dans quelques cas étranges, la réponse de @cnst serait meilleure.
Michael
31

Ou mettez-le simplement sur une seule ligne, comme vous l' makeaimez:

if [ -a myApp ]; then rm myApp; fi;
Jeroen
la source
c'est une excellente réponse dans ce cas (et devrait obtenir des votes positifs) mais ne fonctionnera pas si bien si les actions étaient plus complexes, auquel cas il suffit de passer à l'utilisation de \
Michael
[-a myApp] && rm myApp
Robin Hsu
14

Manque un point-virgule

if [ -a myApp ];
then
  rm myApp
fi

Cependant, je suppose que vous vérifiez l'existence avant la suppression pour éviter un message d'erreur. Si tel est le cas, vous pouvez simplement utiliser la rm -f myAppsuppression qui "force", c'est-à-dire ne pas se tromper si le fichier n'existait pas.

drysdam
la source
1
C'est exactement ce que je voulais faire. Merci beaucoup.
Abruzzo Forte e Gentile
1
cela ne fonctionnera pas dans un Makefile car le if est toujours réparti sur plusieurs lignes - vous devez soit le mettre sur une ligne, soit utiliser \ es. et même si vous avez ajouté le \ es, il vous manque encore des points-virgules.
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)

résultats d'exécution:

bash> make
/usr/bin/perl exists.
/nofile does not exist.
Robin Hsu
la source
Cela m'a aidé, je pense que certains ont manqué que l'OP posait des questions sur makefile. Je n'ai pas compris pourquoi "&& echo -n yes" est nécessaire. Explication: si test -e renvoie 1 (introuvable), le shell n'exécutera pas la commande echo et ne correspondra donc pas au oui dans ifeq
Brad Dre
Je pense que vous l'avez bien compris dans votre déclaration d'explication.
Robin Hsu
@BradDre - Modifie echo -n yesle succès de testdans la chaîne yessans NL. Le ifeqpeut alors le comparer avec le codé en dur yes. Tout cela parce que ifeqveut une chaîne à comparer, pas un statut de réussite d'une commande shell.
Jesse Chisholm
8

Solution une ligne:

   [ -f ./myfile ] && echo exists

Solution en une ligne avec action d'erreur:

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

Exemple utilisé dans mes make cleandirectives:

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

Et make cleanfonctionne toujours sans aucun message d'erreur!

Antonio Feitosa
la source
3
faites simplement @rm -f monfichier. En raison de l'indicateur "-f", "rm" se terminera avec 0, que le fichier existe ou non.
Alexey Polonsky
Ou, -rm myfilele tiret principal indiquant à faire d'ignorer toute erreur.
Jesse Chisholm
1
Dans mon cas, en laissant le || <action d'erreur> a causé des problèmes. Votre dernier exemple, où vous avez renvoyé vrai si le fichier n'existait pas, a bien répondu à cela. Je vous remercie!
Flic
Pour moi, le chemin vers myfile doit être avec apostrophe ('):@[ -f 'myfile' ] && rm myfile
Sten
0
test ! -f README.md || echo 'Support OpenSource!' >> README.md

"Si README.md n'existe pas, ne faites rien (et quittez avec succès). Sinon, ajoutez du texte à la fin."

Si vous utilisez à la &&place de, ||vous générez une erreur lorsque le fichier n'existe pas:

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

J'essayais:

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

Et le cas positif a fonctionné mais mon shell ubuntu bash appelle cela VRAI et se brise sur la copie:

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

Après avoir obtenu cette erreur, je cherche sur Google comment vérifier si un fichier existe dans make, et c'est la réponse ...

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

Cette solution publiée ci-dessus fonctionne le mieux. Mais assurez-vous que vous ne stringify pas l'affectation PATH_TO_FILE Par exemple,

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

Ce doit être

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

Les réponses comme celle de @ mark-wilkins utilisant \ pour continuer les lignes et; les terminer dans le shell ou comme ceux de @kenorb en changeant cela en une seule ligne sont bons et résoudront ce problème.

il existe une réponse plus simple au problème d'origine (comme l'a souligné @ alexey-polonsky). Utilisez l'indicateur -f pour rm afin qu'il ne déclenche pas d'erreur

rm -f myApp

c'est plus simple, plus rapide et plus fiable. Faites juste attention à ne pas vous retrouver avec une barre oblique et une variable vide

rm -f / $ (myAppPath) #NE JAMAIS FAIRE CELA

vous pourriez finir par supprimer votre système.

Michael
la source
1
C'est une bonne réponse pour supprimer le fichier que l'OP avait, mais je suis presque sûr que la plupart des gens qui trouvent cette question ne cherchent pas réellement à supprimer des fichiers; ceci est en fait démontré par le fait que la plupart des réponses ne mentionnent même pas rmdu tout; BTW, je suis à peu près sûr que rm -f /$(myAppPath)cela ne causera aucun dommage non plus, car il /s'agit d'un répertoire et qu'il -rmanque.
cnst
Ce n’est pas une solution plus simple. Comme @cnst l'a dit, il peut y avoir des raisons pour lesquelles l'affiche originale ne veut pas simplement faire un rm -f, par exemple, il peut vouloir rmune variable.
Dan Nguyen