Abruzzo Forte e Gentile
Abruzzo Forte e Gentile

Reputation: 14869

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

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

Upvotes: 204

Views: 318616

Answers (16)

holms
holms

Reputation: 9560

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 

See also:

Update:

I found a way which I really like:

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

Upvotes: 265

Michael Lee
Michael Lee

Reputation: 336

We can also take advantages from GNU Coreutils's test builtin (manual), which means we can check file existence like this:

check_exist:
    test -f file && echo yes || echo no 

or more neatly, like this:

check_exist:
    [ -f file ] && echo yes || echo no 

Upvotes: 0

Brian Brown
Brian Brown

Reputation: 9

I wanted to command above, but reputation :)

You can have multi-line commands in gnu make targets by adding the .ONESHELL: directive:

all-in-one-shell:
    if [ -a MyApp ] ; then
        echo "here"
    fi

.ONESHELL: all-in-one-shell

This eliminates trying to come up with creative one-liners or backslash everything.

Upvotes: 0

Om Narasimhan
Om Narasimhan

Reputation: 53

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

This solution posted above works best. But make sure that you do not stringify the PATH_TO_FILE assignment E.g.,

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

It must be

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

Upvotes: 5

Alex
Alex

Reputation: 77

Use test command to check if the file exists or not and then use rm to delete it.\

Syntax for the file command is -

test -f FILENAME && echo exists || echo not exists

Syntax for deleting the file is -

rm -rf FILENAME

So now we need a command to delete the file only if it exists so we will only use OR || with the test command

test -f FILENAME || rm -rf FILENAME

use can use multiple commands by using and && within the parenthesis ()

test -f FILENAME || (rm -rf FILENAME && echo "file deleted as it exists")

Upvotes: 3

drysdam
drysdam

Reputation: 8637

Missing a semicolon

if [ -a myApp ];
then
  rm myApp
fi

However, I assume you are checking for existence before deletion to prevent an error message. If so, you can just use rm -f myApp which "forces" delete, i.e. doesn't error out if the file didn't exist.

Upvotes: 14

user2585716
user2585716

Reputation:

One line solution:

   [ -f ./myfile ] && echo exists

One line solution with error action:

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

Example used in my make clean directives:

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

And make clean works without error messages!

Upvotes: 28

Jeroen Ooms
Jeroen Ooms

Reputation: 32978

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

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

Upvotes: 38

Mark Wilkins
Mark Wilkins

Reputation: 41222

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;
       

Upvotes: 55

kenorb
kenorb

Reputation: 166477

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.

Upvotes: 84

Robin Hsu
Robin Hsu

Reputation: 4484

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)

execution results:

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

Upvotes: 18

cnst
cnst

Reputation: 27218

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

Upvotes: 120

Eelco van Vliet
Eelco van Vliet

Reputation: 1238

Slightly different from the question, but in case you have a variable containing a list of files which you want to delete you can do

targets: filename1 filename2

clean_targets:
    @$(foreach file, $(targets), test -f $(file) && rm -v $(file) || echo No $(file);)

The basically you loop over the filenames defined by the targets variable and check with 'test' if the target exists. If yes, delete the file, if not, report it is not there. The last check (reporting it is not there) is necessary because otherwise an error is raised in case there is no target at all

Upvotes: 0

Scott Milano
Scott Milano

Reputation: 51

I was trying:

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

And the positive case worked but my ubuntu bash shell calls this TRUE and breaks on the copy:

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

After getting this error, I google how to check if a file exists in make, and this is the answer...

Upvotes: 0

Matt Janssen
Matt Janssen

Reputation: 1663

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

"If README.md does not exist, do nothing (and exit successfully). Otherwise, append text to the end."

If you use && instead of || then you generate an error when the file doesn't exist:

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

Upvotes: 1

Michael
Michael

Reputation: 387

The answers like the one from @mark-wilkins using \ to continue lines and ; to terminate them in the shell or like the ones from @kenorb changing this to one line are good and will fix this problem.

there's a simpler answer to the original problem (as @alexey-polonsky pointed out). Use the -f flag to rm so that it won't trigger an error

rm -f myApp

this is simpler, faster and more reliable. Just be careful not to end up with a slash and an empty variable

rm -f /$(myAppPath) #NEVER DO THIS

you might end up deleting your system.

Upvotes: 1

Related Questions