How could I remove sub-contents of the result of find command on linux - linux

I know I could find the files or directories with Linux command:
find ./ -name '*build*'
and I know I could delete it with -exec option:
find ./ -name '*build*' -exec rm -rf {} \;
The problem is that I need to keep the folder of build but to delete all its contents. Such that I will have some empty build/ directories left.
How could I do this then ?

You can use -execdir to execute something in the directory where a match was found.
find . -name '*build*' -execdir sh -c 'rm -rf {}/*' \;
The (slighty fugly) subshell is required in order for the wildcard to only get expanded when the -execdir happens.

Related

How to "rm -rf" with excluding files and folders with the "find -o" command

I'm trying to use the find command, but still can't figure out how to pipe the find ... to rm -rf
Here is the directory tree for testing:
/path/to/directory
/path/to/directory/file1_or_dir1_to_exclude
/path/to/directory/file2_or_dir2_to_exclude
/path/to/directory/.hidden_file1_or_dir1_to_exclude
/path/to/directory/.hidden_file2_or_dir2_to_exclude
/path/to/directory/many_other_files
/path/to/directory/many_other_directories
Here is the command for removing the whole directory:
rm -rf /path/to/directory
But how to rm -rf while excluding files and folders?
Here is the man help for reference:
man find
-prune True; if the file is a directory, do not descend into it. If
-depth is given, then -prune has no effect. Because -delete im‐
plies -depth, you cannot usefully use -prune and -delete to‐
gether.
For example, to skip the directory `src/emacs' and all files
and directories under it, and print the names of the other files
found, do something like this:
find . -path ./src/emacs -prune -o -print
What's the -o in this find command? Does it mean "or"? I can't find the meaning of -o in the man page.
mkdir -p /path/to/directory
mkdir -p /path/to/directory/file1_or_dir1_to_exclude
mkdir -p /path/to/directory/file2_or_dir2_to_exclude
mkdir -p /path/to/directory/.hidden_file1_or_dir1_to_exclude
mkdir -p /path/to/directory/.hidden_file2_or_dir2_to_exclude
mkdir -p /path/to/directory/many_other_files
mkdir -p /path/to/directory/many_other_directories
I have tried to use this find command to exclude the .hidden_file1_or_dir1_to_exclude and then pipe it to rm, but this command does not work as expected.
cd /path/to/directory
find . -path ./.hidden_file1_or_dir1_to_exclude -prune -o -print | xargs -0 -I {} rm -rf {}
The meaning of rm -rf is to recursively remove everything in a directory tree.
The way to avoid recursively removing everything inside a directory is to get find to enumerate exactly the files you want to remove, and nothing else (and then of course you don't need rm at all; find knows how to remove files, too).
find . -depth -path './.hidden_file1_or_dir1_to_exclude/*' -o -delete
Using -delete turns on the -depth option, which disables the availability of -prune; but just say "delete if not in this tree" instead. And indeed, as you seem to have discovered already, -o stands for "or".
The reason -delete enables -depth should be obvious; you can't traverse the files inside a directory after you have deleted it.
As an aside, you need to use -print0 if you use xargs -0. (This facility is a GNU extension, and generally not available on POSIX.)
You need to separate files from directories to exclude:
find . -mindepth 1\
\( -path ./dir_to_exclude -o\
-path ./.hidden_dir_to_exclude \) -type d -prune\
-o\
! \( -path ./file_to_exclude -o\
-path ./.hidden_file_to_exclude \)\
-exec echo rm -rf {} \;
You can remove the echo once tested.

find command in bash script resulting in "No such file or directory" error only for directories?

UPDATE 2014-03-21
So I realized I wasn't as efficient as I could be, as all the disks that I needed to "scrub" were under /media and named "disk1, disk2,disk3, etc." Here's the final script:
DIRTY_DIR="/media/disk*"
find $DIRTY_DIR -depth -type d -name .AppleDouble -exec rm -rf {} \;
find $DIRTY_DIR -depth -type d -name .AppleDB -exec rm -rf {} \;
find $DIRTY_DIR -depth -type d -name .AppleDesktop -exec rm -rf {} \;
find $DIRTY_DIR -type f -name ".*DS_Store" -exec rm -f {} \;
find $DIRTY_DIR -type f -name ".Thumbs.db" -exec rm -f {} \; # I know, I know, this is a Windows file.
Next will probably to just clean up the code even more, and add features like logging and reporting results (through e-mail or otherwise); excluding system and directories; and allowing people to customize the list of files/directories.
Thanks for all the help!
UPDATE
Before I incorporated the helpful suggestions provided by everyone, I performed some tests, the results of which were very interesting (see below).
As a test, I ran this command:
root#doi:~# find /media/disk3 -type d -name .AppleDouble -exec echo rm -rf {} \;
The results (which is what I expected):
rm -rf /media/disk3/Videos/Chorus/.AppleDouble
However, when I ran the actual command (without echo):
root#doi:~# find /media/disk3 -type d -name .AppleDouble -exec rm -rf {} \;
I received the same "error" output:
find: `/media/disk3/Videos/Chorus/.AppleDouble': No such file or directory
I put "error" in quotes because obviously the folder was removed, as verified by immediately running:
root#doi:~# find /media/disk3 -type d -name .AppleDouble -exec rm -rf {} \;
root#doi:~#
It seems like the find command stored the original results, acted on it by deleting the directory, but then tried to delete it again? Or is the -f option of rm, which is supposed to be for ignoring nonexistent files and arguments, is ignored? I note that when I run tests with the rm command alone without the find command, everything worked as expected. Thus, directly running rm -rf ... \nonexistent_directory, no errors were returned even though the "non_existent_directory" was not there, and directly running rm -r \nonexistent_directory provided the expected:
rm: cannot remove 'non_existent_directory': No such file or directory
Should I use the -delete option instead of the -exec rm ... option? I had wanted to make the script as broadly applicable as possible for systems that didn't have -delete option for find.
Lastly, I don't presume it matters if /media/disk1, /media/disk2, ... are combined in an AUFS filesystem under /media/storage as the find command is operating on the individual disks themselves?
Thanks for all the help so far, guys. I'll publish the script when I'm done.
ORIGINAL POST
I'm writing a bash script to delete a few OS X remnants on my Lubuntu file shares. However, when executing this:
...
BASE_DIR="/media/disk" # I have 4 disks: disk1, disk2, ...
COUNTER=1
while [ $COUNTER -lt 5 ]; do # Iterate through disk1, disk2, ...
DIRTY_DIR=${BASE_DIR}$COUNTER # Look under the current disk counter /media/disk1, /media/disk2, ...
find $DIRTY_DIR -name \.AppleDouble -exec rm -rf {} \; # Delete all .AppleDouble directories
find $DIRTY_DIR -name ".*DS_Store" -exec rm -rf {} \; # Delete all .DS_Store and ._.DS_Store files
COUNTER=$(($COUNTER+1))
done
...
I see the following output:
find: /media/disk1/Pictures/.AppleDouble: No such file or directory
Before I added the -exec rm ... portion the script found the /media/disk1/Pictures/.AppleDouble directory. The script works properly for removing DS_Store files, but what am I missing for the find command for directories?
I'm afraid to screw too much with the -exec portion as I don't want to obliterate directories in error.
tl;dr - Pass -prune if you're deleting directories using find.
For anyone else who stumbles on this question. Running an example like this
find /media/disk3 -type d -name .AppleDouble -exec rm -rf {} \;
results in an error like
rm: cannot remove 'non_existent_directory': No such file or directory
When finding and deleting directories with find, you'll often encounter this error because find stores the directory to process subdirectories, then deletes it with exec, then tries to traverse the subdirectories which no longer exist.
You can either pass -maxdepth 0 or -prune to prevent this issue. Like so:
find /media/disk3 -type d -name .AppleDouble -prune -exec rm -rf {} \;
Now it deletes the directories without any errors. Hurray! :)
You don't need to escape DOT in shell glob as this is not regex. So use .AppleDouble instead of \.AppleDouble:
find $DIRTY_DIR -name .AppleDouble -exec rm -rf '{}' \;
PS: I don't see anywhere $COUNTER being incremented in your script.

Remove all files under cache directory using find

I'm trying to use find to remove all files under cache directories in a web hosted environment.
find /home/hosted -maxdepth 2 -type d -name "cache" -print -exec rm -rf "{}/*" \;
I've tried several variations of this, but for some reason find won't remove the cache/* files. Anyone see anything I'm missing?
Thanks
Arguments for -exec don't get expanded as you expect. That is because -exec calls execve() directly and thus * does not get expanded to all files in a matching directory. If you want to have shell expansion, you have to feed -exec with /bin/sh (or a shell of your choice), like this:
find /your/dir -name "cache" -type d -maxdepth 2 -print -exec sh -c "rm -f {}/*" \;

How to remove folders with a certain name

In Linux, how do I remove folders with a certain name which are nested deep in a folder hierarchy?
The following paths are under a folder and I would like to remove all folders named a.
1/2/3/a
1/2/3/b
10/20/30/a
10/20/30/b
100/200/300/a
100/200/300/b
What Linux command should I use from the parent folder?
If the target directory is empty, use find, filter with only directories, filter by name, execute rmdir:
find . -type d -name a -exec rmdir {} \;
If you want to recursively delete its contents, replace -exec rmdir {} \; with -delete or -prune -exec rm -rf {} \;. Other answers include details about these versions, credit them too.
Use find for name "a" and execute rm to remove those named according to your wishes, as follows:
find . -name a -exec rm -rf {} \;
Test it first using ls to list:
find . -name a -exec ls {} \;
To ensure this only removes directories and not plain files, use the "-type d" arg (as suggested in the comments):
find . -name a -type d -exec rm -rf {} \;
The "{}" is a substitution for each file "a" found - the exec command is executed against each by substitution.
This also works - it will remove all the folders called "a" and their contents:
rm -rf `find . -type d -name a`
I ended up here looking to delete my node_modules folders before doing a backup of my work in progress using rsync. A key requirements is that the node_modules folder can be nested, so you need the -prune option.
First I ran this to visually verify the folders to be deleted:
find . -type d -name node_modules -prune
Then I ran this to delete them all:
find . -type d -name node_modules -prune -exec rm -rf {} \;
Thanks to pistache
To delete all directories with the name foo, run:
find -type d -name foo -a -prune -exec rm -rf {} \;
The other answers are missing an important thing: the -prune option. Without -prune, GNU find will delete the directory with the matching name and then try to recurse into it to find more directories that match. The -prune option tells it to not recurse into a directory that matched the conditions.
This command works for me. It does its work recursively
find . -name "node_modules" -type d -prune -exec rm -rf '{}' +
. - current folder
"node_modules" - folder name
find ./ -name "FOLDERNAME" | xargs rm -Rf
Should do the trick. WARNING, if you accidentally pump a . or / into xargs rm -Rf your entire computer will be deleted without an option to get it back, requiring an OS reinstall.
Combining multiple answers, here's a command that works on both Linux and MacOS
rm -rf $(find . -type d -name __pycache__)
I had more than 100 files like
log-12
log-123
log-34
....
above answers did not work for me
but the following command helped me.
find . -name "log-*" -exec rm -rf {} \;
i gave -type as . so it deletes both files and folders which starts with log-
and rm -rf deletes folders recursively even it has files.
if you want folders alone
find -type d -name "log-*" -exec rm -rf {} \;
files alone
find -type f -name "log-*" -exec rm -rf {} \;
Another one:
"-exec rm -rf {} \;" can be replaced by "-delete"
find -type d -name __pycache__ -delete # GNU find
find . -type d -name __pycache__ -delete # POSIX find (e.g. Mac OS X)
Earlier comments didn't work for me since I was looking for an expression within the folder name in some folder within the structure
The following works for a folder in a structure like:
b/d/ab/cd/file or c/d/e/f/a/f/file
To check before using rm-rf
find . -name *a* -type d -exec realpath {} \;
Removing folders including content recursively
find . -name *a* -type d -exec rm -rf {} \;
find path/to/the/folders -maxdepth 1 -name "my_*" -type d -delete

How to remove all .svn directories from my application directories

One of the missions of an export tool I have in my application, is to clean all .svn directories from my application directory tree. I am looking for a recursive command in the Linux shell that will traverse the entire tree and delete the .svn files.
I am not using export, as this script will be used for some other file/directory names which are not related to SVN. I tried something like:
find . -name .svn | rm -fr
It didn't work...
Try this:
find . -name .svn -exec rm -rf '{}' \;
Before running a command like that, I often like to run this first:
find . -name .svn -exec ls '{}' \;
What you wrote sends a list of newline separated file names (and paths) to rm, but rm doesn't know what to do with that input. It's only expecting command line parameters.
xargs takes input, usually separated by newlines, and places them on the command line, so adding xargs makes what you had work:
find . -name .svn | xargs rm -fr
xargs is intelligent enough that it will only pass as many arguments to rm as it can accept. Thus, if you had a million files, it might run rm 1,000,000/65,000 times (if your shell could accept 65,002 arguments on the command line {65k files + 1 for rm + 1 for -fr}).
As persons have adeptly pointed out, the following also work:
find . -name .svn -exec rm -rf {} \;
find . -depth -name .svn -exec rm -fr {} \;
find . -type d -name .svn -print0|xargs -0 rm -rf
The first two -exec forms both call rm for each folder being deleted, so if you had 1,000,000 folders, rm would be invoked 1,000,000 times. This is certainly less than ideal. Newer implementations of rm allow you to conclude the command with a + indicating that rm will accept as many arguments as possible:
find . -name .svn -exec rm -rf {} +
The last find/xargs version uses print0, which makes find generate output that uses \0 as a terminator rather than a newline. Since POSIX systems allow any character but \0 in the filename, this is truly the safest way to make sure that the arguments are correctly passed to rm or the application being executed.
In addition, there's a -execdir that will execute rm from the directory in which the file was found, rather than at the base directory and a -depth that will start depth first.
No need for pipes, xargs, exec, or anything:
find . -name .svn -delete
Edit: Just kidding, evidently -delete calls unlinkat() under the hood, so it behaves like unlink or rmdir and will refuse to operate on directories containing files.
There are already many answers provided for deleting the .svn-directory. But I want to add, that you can avoid these directories from the beginning, if you do an export instead of a checkout:
svn export <url>
If you don't like to see a lot of
find: `./.svn': No such file or directory
warnings, then use the -depth switch:
find . -depth -name .svn -exec rm -fr {} \;
In Windows, you can use the following registry script to add "Delete SVN Folders" to your right click context menu. Run it on any directory containing those pesky files.
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Folder\shell\DeleteSVN]
#="Delete SVN Folders"
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Folder\shell\DeleteSVN\command]
#="cmd.exe /c \"TITLE Removing SVN Folders in %1 && COLOR 9A && FOR /r \"%1\" %%f IN (.svn) DO RD /s /q \"%%f\" \""
You almost had it. If you want to pass the output of a command as parameters to another one, you'll need to use xargs. Adding -print0 makes sure the script can handle paths with whitespace:
find . -type d -name .svn -print0|xargs -0 rm -rf
find . -name .svn |xargs rm -rf
As an important issue, when you want to utilize shell to delete .svn folders You need -depth argument to prevent find command entering the directory that was just deleted and showing error messages like e.g.
"find: ./.svn: No such file or directory"
As a result, You can use find command like below:
cd [dir_to_delete_svn_folders]
find . -depth -name .svn -exec rm -fr {} \;
Try this:
find . -name .svn -exec rm -v {} \;
Read more about the find command at developerWorks.
Alternatively, if you want to export a copy without modifying the working copy, you can use rsync:
rsync -a --exclude .svn path/to/working/copy path/to/export

Resources