What does the -exec option to 'find' do? - shell

I am new to Makefile. I was going through an existing makefile and couldn't understand what it does. The line is as below.
find $(RELEASE_DIR) -depth -name "*CVS" -exec rm -rf {} \;
find command is used to find the strings. But I could not understand what this line exactly do. Please help to understand.

The find command is there to search for files in a given directory.
find <directory> -option1 -option2
The option -name "*CVS" says that the command will search for files with CVS in the end of their name.
-depth means that the directories are traversed with the http://en.wikipedia.org/wiki/Depth-first_search method.
-exec rm -rf {} \; tells find to execute the command rm -rf for every file that was found. {} is a placeholder for the currently found file and \; marks the end of the rm command.

This mean that it will scan the $(RELEASE_DIR) and for each file that have a name like *CVS we execute rm -rf, this mean delete it.
=> This command delete all files that contain CVS in the end of their name.

Related

How to concatenate string to -print in unix find command?

I have this command to run in Unix machine where I'm deleting all directories from specific folder if they are older then 1 day:
find /path_with_dirs_to_delete/* -type d -mtime +1 -prune -exec rm -rf {} + -print
It's currently printing out the folder names and i want to concatenate to the printing output the text " was deleted successfully".
How can I do that?
If using GNU tools, a superior solution is to add the -v (verbose) flag to rm.
For a POSIX solution you could do this:
find /path/* -type d -mtime +1 -prune -exec rm -rf {} \; -exec echo {} was deleted successfully \;
You were running a single rm to remove multiple directories. But if you want to test rm's success for each directory, it has to run once per directory (note ; instead of +). Note that this a bit less efficient.
-a ("and") is implied between find actions, meaning the second -exec action (echo) only runs if the previous one (rm) succeds. So this can be used to log a successful removal.
You will receive an error message from rm if a removal fails (eg. permission denied).
rm -f suppresses "does not exist" errors, and you wouldn't get a success message either, however find won't pass a non-existent directory.

Why does "find . -name somedir -exec rm -r {} \;" exit nonzero whenever it successfully deletes a directory?

I want to find a folder in a known folder but with unknown exact path, thus I must use find. When I find it, I use -exec to remove it, but I cannot evaluate if this has succeeded. Behavior somewhat confuses me; if I don't find the folder, I get return code 0:
>find . -name "testuuu" -exec rm -r {} \;
find: ‘./.docker’: Permission denied
>echo $?
0
But when I do find the folder and manage to delete it, it returns error code 1:
> find . -name "test" -exec rm -r {} \;
find: ‘./.docker’: Permission denied
find: ‘./test’: No such file or directory
> echo $?
1
Is this expected behavior? Why?
Further, how can I get return code of "rm" and not "find" and thus evaluate if the folder was deleted?
Use the -depth option to order a depth-first traversal -- that way find doesn't try to find things under your directory after the directory has already been deleted (an operation which, by nature, will always fail).
find . -depth -name "test" -exec rm -r -- '{}' \;
This option is also turned on by default when you use the -delete action in GNU find to delete content.
By the way -- if there are lots of test directories, you could get better performance by making that -exec rm -r -- {} + (which passes each copy of rm as many filenames as will fit on its command line) instead of -exec rm -r -- {} \; (which starts a new copy of rm for each test that is found), at the expense of no longer collecting individual error codes.

Delete nested subdirectories by name on Mac?

How can I delete all subdirectories of a certain name via the command line on mac?
Eg if I have
/parent/foo
/parent/child/foo
/parent/child/child2/foo
How can I run a command from /parent that will delete any folder called foo and all of it's contense?
Ive tried this command but no luck:
find . -type d -name foo -delete
Using find you can do:
find . -d -type d -name a -exec rm -r {} \;
As you have discovered -delete does not appear to delete non-empty directories (so its equivalent to rmdir not rm -r), though this may not be documented.
The -exec executes a command, {} is replaced by the matching pathname, and \; marks the end of the command. So this does a recursive removal of every matching path.
The -d sets depth first traversal which means directories are processed after their contents. If you omit this find will first remove the directory and then try to recurse into it – this results in error messages but still works.
There is a shorter way using zsh:
rm -r **/a
The pattern ** does a search an matches a at any depth.
If zsh is not your default shell you can use its -c argument to run a command from another shell:
zsh -c 'rm -r **/a'
HTH
The name of the directory has to be in quotes.
Do the following:
find . -type d -name "foo" -delete

Running a bash find with file cp parameter error python script

I'd like to copy a file_list to another location. This is being called in a python script. I have
find <sourceaddress> -exec cp '{}' <destaddress> | .* rm
but it tells me an exact parameter is missing. It runs though it gives a prompt from the command line and from the script just does nothing.
I think you are missing "\;" at the end. I am not sure what the .* rm does. Assuming you want to remove the files you can use the 'mv' command instead of 'cp'.
For copying files only from one directory to another ,
find <srcdirectory> -exec cp '{}' <destdirectory> \;
If you want to move the files, use 'mv' instead use below.
find <srcdirectory> -exec mv '{}' <destdirectory> \;

Copy all files with a certain extension from all subdirectories

Under unix, I want to copy all files with a certain extension (all excel files) from all subdirectories to another directory. I have the following command:
cp --parents `find -name \*.xls*` /target_directory/
The problems with this command are:
It copies the directory structure as well, and I only want the files (so all files should end up in /target_directory/)
It does not copy files with spaces in the filenames (which are quite a few)
Any solutions for these problems?
--parents is copying the directory structure, so you should get rid of that.
The way you've written this, the find executes, and the output is put onto the command line such that cp can't distinguish between the spaces separating the filenames, and the spaces within the filename. It's better to do something like
$ find . -name \*.xls -exec cp {} newDir \;
in which cp is executed for each filename that find finds, and passed the filename correctly. Here's more info on this technique.
Instead of all the above, you could use zsh and simply type
$ cp **/*.xls target_directory
zsh can expand wildcards to include subdirectories and makes this sort of thing very easy.
From all of the above, I came up with this version.
This version also works for me in the mac recovery terminal.
find ./ -name '*.xsl' -exec cp -prv '{}' '/path/to/targetDir/' ';'
It will look in the current directory and recursively in all of the sub directories for files with the xsl extension. It will copy them all to the target directory.
cp flags are:
p - preserve attributes of the file
r - recursive
v - verbose (shows you whats
being copied)
I had a similar problem. I solved it using:
find dir_name '*.mp3' -exec cp -vuni '{}' "../dest_dir" ";"
The '{}' and ";" executes the copy on each file.
I also had to do this myself. I did it via the --parents argument for cp:
find SOURCEPATH -name filename*.txt -exec cp --parents {} DESTPATH \;
In 2022 the zsh solution also works in Linux Bash:
cp **/*.extension /dest/dir
works as expected.
find [SOURCEPATH] -type f -name '[PATTERN]' |
while read P; do cp --parents "$P" [DEST]; done
you may remove the --parents but there is a risk of collision if multiple files bear the same name.
On macOS Ventura 13.1, on zsh, I saw the following error when there were too many files to copy, saw the following error:
zsh: argument list too long: cp
Had to use find command along with cp to get the files copied to my destination:
find ./module/*/src -name \*.java -print | while read filelocation; do cp $filelocation mydestinationlocation; done

Resources