How do I check if file exists in Makefile so I can delete it?
MakefileMakefile Problem Overview
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
Makefile Solutions
Solution 1 - Makefile
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 2 - Makefile
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 3 - Makefile
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 - Makefile
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 - Makefile
Or just put it on one line, as make
likes it:
if [ -a myApp ]; then rm myApp; fi;
Solution 6 - Makefile
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!
Solution 7 - Makefile
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.
Solution 8 - Makefile
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.
Solution 9 - Makefile
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
Solution 10 - Makefile
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")
Solution 11 - Makefile
I wanted to command above, but reputation :)
You can have multi-line commands in gnu make targets by adding the .ONESHELL:
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.
Solution 12 - Makefile
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
Solution 13 - Makefile
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...
Solution 14 - Makefile
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
Solution 15 - Makefile
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.