find ~ -name delete_me -type f -print0 | xargs -0 /bin/rm -f
Q1.What does it meant by -print0, why is it required ?
Q2.Why /bin/rm ? why not just rm -f ?
This command only delete matched file in ~ directory; it won't delete matched file in subdirectory or sub sub directory..
Q3. How to modify it so that it will find and delete all the matched file in subdirectories as well ?
Q4. How to modify it so that it will not find into a certain directory ?
Thanks
The non-standard -print0 predicate means "print the pathname of the found file with a terminating nul character at the end instead of a newline character". This is to be able to handle filenames that contain newline characters (which is permitted on Unix systems; nul characters can however not be part of a Unix filename). The command reading the output of the find command that uses -print0 needs to be aware that the entries are nul-terminated. It's the -0 option to xargs that lets it read nul-terminated entries.
It is not required. Neither of
find "$HOME" -name delete_me -type f -delete
nor the more standard
find "$HOME" -name delete_me -type f -exec rm {} +
uses -print0. Both commands do the same thing as your command, and should be preferred to using xargs. The xargs utility could possibly be useful when reading arguments from a file or some other command. When using find, it is safer to use its built in -exec facility. There are a few thing that some implementation of xargs can do that -exec can't, like starting concurrent batches of utilities. This is however not used in this exercise.
It is also preferrable to use "$HOME" in place of ~ in shell scripts, as $HOME behaves like a variable, and ~ does not.
Also note that the -f option to rm probably isn't needed. It is used to avoid having the rm utility fail (with a diagnostic message and a non-zero exit status) if the given pathnames are missing, or if rm is called with no pathnames to remove. It also overrides any earlier -i (interactive mode) options. But there is no -i option on the rm command line here, and the given pathnames are guaranteed to exist as find is being used to find them (barring race conditions where the files are actually deleted during the execution of find, by some other process, between being found by find and being deleted by the rm executed from find).
There is no reason to use rm with its full path, unless the PATH variable's value can not be guaranteed to contain a sane set of utility paths. It is unusual to have a broken PATH and writing scripts that use absolute paths for utilities are prone to break when they are moved to other systems.
It does already recurse down into all subdirectories of your home directory.
Prune the directory that you want to avoid from the search tree. For example, to avoid all directories called avoid:
find "$HOME" -name avoid -prune -o -name delete_me -type f -exec rm {} +
Note that we can't use -delete here as this implies -depth, i.e. a depth-first search. With a depth-first search, we'd be visiting all the subdirectories and files inside avoid before noticing that the avoid directory should be pruned from the search tree.
Another example: Don't enter into the specific directory $HOME/dir/save_me:
find "$HOME" -path "$HOME/dir/save_me" -prune -o -name delete_me -type f -exec rm {} +
-o means "or". When there's nothing between two tests/predicates, it means "and". Tests are evaluated left-to-right. The command that you show will most definitely recurse, or you are running some other command that you do not show (possibly just subtly different). - Kusalananda
find. If you want to ask about fdfind, ask a new question. - Kusalananda
find will only iterate one object at a time starting from the starting-point. -name or -path will only test the pattern on this particular object and not on the list in its entirety. You may not search for two or more unique file objects on the same iteration, e.g., if you have a path './1/a/b', then, the first object will be './1', second './1/a, and the last './1/a/b', this is why find -name 'a' -name 'b' will never work, sure, you can combine tests, e.g., find -path './1/*' -name 'c' will match the third. - user380915
unstable group), and then fd resolves to fdsh. Care is advised. - Paul_Pedant
ACCEPTED]
Q1.What does it meant by -print0, why is it required ?
A good place to start is by looking in the manual. At the terminal you can type man find an get a good description. Typically you can also just google for "man find" and get the same / similar manual pages on the web.
The manual for find says about -print0:
print the full file name on the standard output, followed by a null character (instead of the newline character that -print uses). This allows file names that contain newlines or other types of white space to be correctly interpreted by programs that process the find output.
So this writes every file name followed by a null character and xargs with -0 will read, expecting each item to end with -0. This allows file names to contain a new line character.
Q2.Why /bin/ ? why not just rm -f ?
It's not obvious why this was done from your question.
It looks like there's really no good reason for this. One reason for doing this sometimes is to let you run a comment when the
PATH
[1] environment variable is not set correctly. This can be common when running commands in in cron.
However in this case it isn't helping anything since other commands (find and xargs) are not fully qualified. It should be:
/bin/find ~ -name delete_me -type f -print0 | /usr/xargs -0 /bin/rm -f
The only other (highly unlikely) reason would be that your system somehow has two different rm commands and you need to specify which one. But that really is very unlikely.
Q3. How to modify it so that it will find and delete all the matched file in subdirectories as well ?
It already does this.
Q4. How to modify it so that it will not find into a certain directory ?
You can use the -prune option to skip that directory (and its children).
# Just editing your version...
find "$HOME" -name exclude_this_dir -prune -o -name delete_me -type f -print0 | xargs -0 /bin/rm -f
# More succinctly
find "$HOME" -name exclude_this_dir -prune -o -name delete_me -type f -exec rm -f {} +
[1] https://en.wikipedia.org/wiki/PATH_(variable)find -print0 | xargs -0 approach • Find files named core in or below the directory /tmp and delete them, processing filenames in such a way that file or directory names containing single or double quotes, spaces or newlines are correctly handled. $ find /tmp -name core -type f -print0 | xargs -0 /bin/rm -f . I copied this from man find.. it said suppose to be safer way, safer than -delete method - andrew_ysk
65 b is 66 ... newline is number 10. There is a special character called the "null" character which is 0. It has special meaning and usually cannot be included inside other text. This makes it a safe thing to use to divide up different bits of text, because it will only be the divider, never inside the text itself. In this case, there's an assumption that file names can contain a new line (10) character but cannot contain a null character (0). - Philip Couling
10 after every file name and 10 is read as a new line. I can't say why the author of find's manual wrote it the way they did. I only know it's not necessary. I've explained the reasons why you might do something like that in the answer. But try it yourself, it works just fine without /bin. - Philip Couling
-o. So just add more -name directory_name -prune -o to the front of the arguments. - Philip Couling
man ascii will list the whole character set with char, octal, decimal and hex values, and the common (shell and C and other languages) escape sequences for them. E.g 012 10 0A LF '\n' (new line). - Paul_Pedant