How do I check if file exists in Makefile so I can delete it?

237,235

Solution 1

The second top answer mentions ifeq, however, it fails to mention that this ifeq must be at the same indentation level in the makefile as the name of the target, e.g., to download a file only if it doesn't currently exist, the following code could be used:

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

Solution 2

It's strange to see so many people using shell scripting for this. I was looking for a way to use native makefile syntax, because I'm writing this outside of any target. You can use the wildcard function to check if file exists:

 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 

Update:

I found a way which is really working for me:

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

Solution 3

The problem is when you split your command over multiple lines. So, you can either use the \ at the end of lines for continuation as above or you can get everything on one line with the && operator in bash.

Then you can use a test command to test if the file does exist, e.g.:

test -f myApp && echo File does exist

-f file True if file exists and is a regular file.

-s file True if file exists and has a size greater than zero.

or does not:

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

The test is equivalent to [ command.

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

and it would work as in your original example.

See: help [ or help test for further syntax.

Solution 4

It may need a backslash on the end of the line for continuation (although perhaps that depends on the version of make):

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

Solution 5

Or just put it on one line, as make likes it:

if [ -a myApp ]; then rm myApp; fi;
Share:
237,235

Related videos on Youtube

Abruzzo Forte e Gentile
Author by

Abruzzo Forte e Gentile

Updated on February 04, 2022

Comments

  • Abruzzo Forte e Gentile
    Abruzzo Forte e Gentile 11 months

    In the clean section of my Makefile I am trying to check if the file exists before deleting permanently. I use this code but I receive errors.

    What's wrong with it?

     if [ -a myApp ]
     then
         rm myApp
     fi
    

    I get this error message

     if [ -a myApp ]
     /bin/sh: Syntax error: end of file unexpected (expecting "then")
     make: *** [clean] Error 2
    
    • thomasa88
      thomasa88 almost 9 years
      If you just want to avoid make stopping if the file does not exist, rm -rf myApp could be an alternative. Or preceding the command with a dash (-rm myApp) to make make ignore the error from rm (it will however print an ugly message).
    • Michael
      Michael almost 4 years
      Your problem was that make treats each line in a rule as a separate command and sends them individually to the shell. It's like running just `if [ -a myApp ]' on its own. If you get this error you either need a solution which joins the lines into one (using ) or which ends up with each line independent of the other. There are now several of these below.
  • Abruzzo Forte e Gentile
    Abruzzo Forte e Gentile over 11 years
    ThHat's exactly what I wanted to do. Thanks a lot.
  • holms
    holms about 9 years
  • holms
    holms almost 9 years
    I've got actually alternative variant recently. I'm discovering package managers with this yum := $(shell { type yum; } 2>/dev/null) and with ifdef you can check if variable empty or not, and then set something like RHEL:=true
  • Ant6n
    Ant6n about 8 years
    tried this, but I just keep getting Makefile:133: *** unterminated call to function `wildcard': missing `)'. Stop.
  • flungo
    flungo about 8 years
    @holms its a bash syntax. By escaping the new lines it allows it to be handled as a single bash command. By default in make a new line would be a new bash command. The major caveat of this, other than the annoyance of having lots of \ at the end of lines is that every command must be terminated with the ; which would otherwise be implicit in bash.
  • tom_mai78101
    tom_mai78101 almost 8 years
    @arsenbonbon Your comment really helps. Appreciated.
  • tom_mai78101
    tom_mai78101 almost 8 years
    @Ant6n You might be missing a parentheses. Please check.
  • Maitreya
    Maitreya over 7 years
    The right answer is by @holms. Neither '\' nor wildcard is exactly intended for this purpose. The reason why you would use wildcard is for code clarity. Not only is this method less readable, it's more prone to syntax errors.
  • underscore_d
    underscore_d over 7 years
    I would have upvoted, but you did not warn that -s is a special case for exists and has a size greater than zero. The question as written is size-agnostic, so existence should be checked using test -e for a file or -d for a directory. Empty files can be especially useful as (for want of a better term) indicators/sentinels, which might be quite relevant for make.
  • glerYbo
    glerYbo over 7 years
    Thanks for the suggestion. Changed -f by default, as it's more common to use.
  • thowa
    thowa almost 7 years
    How can I get test on Windows?
  • fero
    fero over 6 years
    the link @holms provided does not work anymore, use gnu.org/software/make/manual/make.html#Conditionals instead
  • serup
    serup over 6 years
    This I could get to work on one line in make file like this: @ test -f file && echo file exist -- however I could not get the other examples to work
  • Christopher
    Christopher over 6 years
    This fails if $(PATH_TO_FILE) evaluates to a filename containing parentheses.
  • Alexey Polonsky
    Alexey Polonsky about 6 years
    just do @rm -f myfile. Because of the "-f" flag, "rm" will exit with 0 regardless of whether the file exists or not.
  • anoop
    anoop over 5 years
    Great use of wildcard, so it can be done with makefile itself. Neat :)
  • jcubic
    jcubic almost 5 years
    In order for this to work you need to add || true at the end so the command return true when file don't exists.
  • alexpanter
    alexpanter over 4 years
    This answer should be upvoted as it is easy, simple, one-line, and solves the problem.
  • Dr. Dan
    Dr. Dan about 4 years
    Helps to also understand what $(wildcard pattern) actually does. See link.
  • Danijel
    Danijel about 4 years
    Is there a way to make this work within a make rule (when "indented")?
  • cmaster - reinstate monica
    cmaster - reinstate monica about 4 years
    More concise: FILE_EXISTS := $(or $(and $(wildcard $(PATH_TO_FILE)),1),0)
  • Roger Sanders
    Roger Sanders about 4 years
    It's worth noting if you're running on Windows under cygwin, using wildcard in this manner does a case sensitive match on the filename, so if the file exists but with different case than the path it won't be found. There doesn't seem to be any way to override this behaviour within the makefile.
  • taras
    taras almost 4 years
    did not work until I added backslash `` after if fi
  • Victor Sergienko
    Victor Sergienko almost 4 years
    directives only work if they're not indented with tabs. Plus, they run at makefile parsing stage, not in "runtime" (target execution) stage.
  • Michael
    Michael almost 4 years
    This answer will be a bit weird; the file check happens when the makefile is processed but the action will happen later when the target is built by make. If you delete the file in the meantime then the file won't be created. I have put in an edit to make it clearer.
  • Michael
    Michael almost 4 years
    this is a great answer because it matches what is wrong with what the original questioner did and it will work depending on whether the file exists at the time the target is build rather than at the time the Makefile is started which is what most people would expect and want most of the time. In a few weird cases the answer from @cnst would be better.
  • Michael
    Michael almost 4 years
    that's a great answer in this case (and should get upvotes) but won't work so well if the actions were more complex, in which case just switch to using \
  • Michael
    Michael almost 4 years
    this won't work in a Makefile because the if is still spread over multiple lines - you need to either put this on one line or use \es. and even if you added the \ es you are still missing some semi-colons.
  • cnst
    cnst almost 4 years
    This is a good answer for deleting the file that the OP had, but I'm pretty sure most people who find this question aren't actually looking for deleting any files; this is actually evidenced by the fact that most answers don't even mention rm at all; BTW, I'm pretty sure that rm -f /$(myAppPath) won't do any damage, either, because / is a directory, and the -r is missing.
  • glerYbo
    glerYbo almost 4 years
    @AndrewMackenzie test -f myApp || CMD, notice the ||, so if -f will fail - does not exist (||), then run the command. Does it make sense?
  • Robin Hsu
    Robin Hsu almost 4 years
    [ -a myApp ] && rm myApp
  • Jesse Chisholm
    Jesse Chisholm over 3 years
    The original question, and this answer are attacking it at the inside an action command phase, whereas the @holms answer is attacking it at the makefile parsing phase. So which answer is best depends on what you need.
  • Jesse Chisholm
    Jesse Chisholm over 3 years
    Or, -rm myfile the lead dash telling make to ignore any error.
  • Flic
    Flic over 3 years
    In my case, leaving off the || <error action> caused problems. Your final example, where you returned true if the file did not exist, addressed this nicely. Thank you!
  • Kevin Buchs
    Kevin Buchs over 3 years
    Thanks much! This point was not clear from reading the manual.
  • Sten
    Sten about 3 years
    For me path to myfile needs to be with apostrophe('): @[ -f 'myfile' ] && rm myfile
  • dancow
    dancow about 3 years
    This is not a simpler solution. Like @cnst said, there may be reasons why the original poster does not simply want to do an rm -f, e.g. they may want to rm a variable.
  • Peter Krauss
    Peter Krauss almost 2 years
    ERROR /bin/sh: 1: [: -a: unexpected operator
  • Chan Kim
    Chan Kim over 1 year
    I like this simple answer. if the file exists delete, if it doesn't exist, don't complain!
  • nathanchere
    nathanchere about 1 year
    This does not work in a makefile. It ill always run the rm -rf FILENAME regardless of the first test result