10

My Makefile:

all: ...(other rules) clean

clean:
    rm $(find . -type f -executable)

When I delete clean rule from the above Makefile everything works as expected. After adding, make (also make clean) command results in:

rm 
rm: missing operand
Try 'rm --help' for more information.
make: *** [Makefile:46: clean] Error 1

What causes problem here and how can I solve?

2
  • 3
    Is there a reason not to use find . -type f -executable -delete? Also note that $ introduces the expansion of a makefile variable.
    – Kusalananda
    Sep 19, 2021 at 10:14
  • @Kusalananda actually I'm not aware of -delete opt in find. Do you say that $ has a special meaning in Makefile and there is no workaround for this? Sep 19, 2021 at 10:21

1 Answer 1

19

There are several issues.

Passing a $ sign in a Makefile to the shell

You want to run the command

rm $(find . -type f -executable)

to let the shell do the command substitution. To do this you need to write

clean:
        rm $$(find . -type f -executable)

with the dollar doubled as Make itself uses $.

Handing the case where there is nothing to "clean"

If the output of find is empty, then after command substitution

rm $(find . -type f -executable)

becomes

rm

and the typical rm command complains that you haven't told it what to remove. One way to address this is to use xargs to process the output of find. It takes the output of find and if there is any it splits it into blocks and runs rm.

clean:
        find . -type f -executable | xargs rm

Handling arbitrary characters in filenames

Unix filenames are made up of components separated by / characters. The components themselves can be any sequence of characters except / and NUL. In particular a component can include newline characters, spaces, tabs, *.

For the command substitution case (rm $(find . -type f -executable)) case the shell will process the output of find. So white-space characters will cause word splitting, * characters will cause filename "globbing" to take place etc. For reasonable implementations of xargs this is avoided.

If filenames begin with - then rm might consider them to be options to the command. The simple way to avoid this is to add -- to the command to indicate "end of options".

The major remaining issue is newlines. xargs splits the input on newlines, so if you have a file called abc\ndef then find will output

abc
def

and xargs will invoke rm with 2 filenamesabc and def.

To work around this, tell both find and xargs to use the 1 character (NUL) that can't appear in filenames as the deliminator rather than newline.

clean:
        find . -type f -executable -print0 | xargs -0 rm --

Best solution, if your find supports it

clean:
        find . -type f -executable -delete

Here you have find directly removing the files, if any. It is more efficient as it doesn't need to start additional processes. There are no shells or xargs processes to need to escape characters. No special characters to tell make to process. Handles the "no files to delete" case correctly.

0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .