Using `find`, how can I get `rm` to print a fuller path name than it does when executed through `-execdir`

find

Summary of Answers: while literature promotes "-execdir" as safer than "-exec", do not ignore the operational difference. The former is not a simple reimplementation of the latter. Also, when using find specifically for interactive removals, executing "rm -f" via the "-okdir" option is also viable. Similarly for slightly more complicated constructs.

I'm trying to learn the "find" command, and I'm having trouble understanding its output. I apologize if this is a duplicate, but the problem statement is a little convoluted and hard to Google.

Let me set up a pet case: I start in some directory (".") with two subdirectories "A" and "B". In both of these, there is a file named "hello.c."

So, for example,

find . -name "hello.c"

would print like so:

./A/hello.c
./B/hello.c 

So far so good. What I get lost on is when I try to do something with the "-execdir" option; let's say I want to use an interactive removal. Then:

find . -name "hello.c" -execdir rm -i {} \;

or similar. What I expect is

rm: remove regular file "./A/hello.c"?

and then, answering that, a similar prompt for the "hello.c" in directory "B."
What actually appears, unfortunately, is

rm: remove regular file "./hello.c"? 

In this tiny example, I can reasonably infer that it's asking about "./A/hello.c", but if one scales this example up then you get dozens of files that all have their pathnames truncated to "./". And I cannot differentiate between dozens of "./hello.c"s, not without a bearing on which subdirectory they each live in.

So, my question boils down to a desire to print fuller pathnames via "-execdir." Could I hear a hint as to what sublety in the manpage escaped me? Little is said about the "{}" substitution. Or if there is some better way to manage this particular case (interactive removal), I should like to hear that too, because I'm not sure my approach is best practice.

Best Answer

The man page for GNU find describes -execdir in part thusly:

Like -exec, but the specified command is run from the subdirectory containing the matched file, which is not normally the directory in which you started find.

So there is no real subtlety involved. -exec invokes the command from the directory that you run find from (and thus needs to provide the path either relative to that directory or absolute from the root), and -execdir invokes the command from the directory containing the file and thus doesn't need to provide the full path, so does not.

To get the behavior you are after, you should use -exec rather than -execdir.

You can demonstrate this behavior by replacing your rm invocation with for example echo to simply print the list of parameters (in this case, the file name). Or use -print which does that without needing to invoke an external command. (Note: -print is also the default action if none is given.)

If you didn't need the confirmation, you could have used -delete instead. For large number of files that is also likely to be more efficient as it avoids having to invoke rm time and time again.