How to write a unix command or script to remove files of the same type in all sub-folders under current directory? - linux

Is there a way to remove all temp files and executables under one folder AND its sub-folders?
All that I can think of is:
$rm -rf *.~
but this removes only temp files under current directory, it DOES NOT remove any other temp files under SUB-folders at all, also, it doesn't remove any executables.
I know there are similar questions which get very well answered, like this one:
find specific file type from folder and its sub folder
but that is a java code, I only need a unix command or a short script to do this.
Any help please?
Thanks a lot!

Perl from command line; should delete if file ends with ~ or it is executable,
perl -MFile::Find -e 'find(sub{ unlink if -f and (/~\z/ or (stat)[2] & 0111) }, ".")'

You can achieve the result with find:
find /path/to/directory \( -name '*.~' -o \( -perm /111 -a -type f \) \) -exec rm -f {} +
This will execute rm -f <path> for any <path> under (and including) /path/to/base/directory which:
matches the glob expression *.~
or which has an executable bit set (be it owner, group or world)
The above applies to the GNU version of find.
A more portable version is:
find /path/to/directory \( -name '*.~' -o \( \( -perm -01 -o -perm -010 -o -perm -0100 \) \
-a -type f \) \) -exec rm -f {} +

find . -name "*~" -exec rm {} \;
or whatever pattern is needed to match the tmp files.

If you want to use Perl to do it, use a specific module like File::Remove

This should do the job
find -type f -name "*~" -print0 | xargs -r -0 rm

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 but it stops going deep if it finds a directory starting with "."

I have to make a script that goes through a whole folder (/home, in my case).
I have to save all the files except the ones that start with ., and also, if I find a directory that starts with ., I don't have to care what's inside, I don't have to read it.
For the first part we use the command
for path in $(find /home \! -name ".*");do
where path is a variable that contains the path. But we don't know how to do the directory part.
I thought I'd cut the path through the / and then see if there's any .. In that case, have an if that does not save the file, but I don't know how to cut a string and save it in a variable and then go through it.
You can prune all files starting with a ..
From the man page of GNU find:
-prune True; if the file is a directory, do not descend into it. If -depth is given, false; no effect. Because -delete implies -depth, you cannot usefully use -prune and -delete together.
You should not loop over the result from find. You will get unexpected results if you have filenames with spaces or newlines.
Use xargs or -exec, e.g.
find /home -path "*/.*" -prune -o -print0 | xargs -0I{} sh -c 'echo "doing something with $1"' sh {}
or
find /home -path "*/.*" -prune -o -exec sh -c 'for i; do echo "doing something with $i"; done' sh {} +
The -prune part removes all filenames (files and directories) starting with a dot and does not descend into directories starting with a dot.
All other filenames are printed with a NUL character instead of a newline (-o -print0) and piped to xargs or a shell script is executed with your action (as few times as possible).
To save all filenames into a file:
find /home -path "*/.*" -prune -o -print > allfiles.txt
Try this
for path in $(find /home -type d -name ".*" -prune -o -type f \! -name ".*" -print);do echo $path; done
I think I would do something like that:
for path in $(find . -type f | egrep -v '/\.[^\/]+\/'); do
...
Note that you may have to take extra steps if some of your files have spaces in their names.

Linux find folder and rename

I want to rename all .hg_gg folders in /var/www to .hg. How can I do it?
I know how to rename .hg to .hg_gg.
find /var/www -name ".hg" -exec bash -c 'mv $0 $0_gg' {} \;
but don't know how to make reverse change.
Try this:
find /var/www -name ".hg_gg" -execdir bash -c 'mv {} .hg' \;
You need to use a special syntax defined by find: {} is the placeholder for the current file name. Check the man page for that. Also it is important to use -execdir instead of -exec. execdir changes the current working directory to the folder where the found directory is located. Otherwise it would do something like this mv /var/www/.hg_gg ./.hg
You can speed up things a bit when restricting find to find folders only using -type d:
find /var/www -type d -name ".hg_gg" -execdir bash -c 'mv {} .hg' \;
Consider this find command with -execdir and -prune options:
find /var/www/ -type d -name ".hg_gg" -execdir mv '{}' '.gg' \; -prune
-execdir will execute the command in each subdirectory
-prune causes find to not descend into the current file
Not a one liner, but you could do this:
for file in `find /var/www -name ".hg_gg"`; do
mv $file `echo $file | sed 's/hg_gg$/hg/'`
done

How to find in linux and delete directories that not match a name? !#Bash

I need to delete unpacked directories from my /source tree keeping the others with .tar and .patch extensions,
how to do please?
This should work:
find . -not -name "*.tar" -not -name "*.patch" -type f -exec rm {} \;
This is using only one command not using pipes.
Note. This will proceed recursively into subdirectories. If this is unwanted, use the maxdepth switch:
find . -maxdepth 1 -not -name "*.tar" -not -name "*.patch" -type f -exec rm {} \;
BACKUP YOUR DIRECTORY FIRST, I HAVE NOT TESTED THIS, AND IT HAS A BUG AS NOTED IN THE COMMENTS.
WHILE IN THE ACTUAL /source DIRECTORY:
ls|fgrep -v -e .tar -e .patch|xargs rm -rf
You probably want to use the "put echo after xargs" trick to see what this would actually do, before running it:
ls|fgrep -v -e .tar -e .patch|xargs echo rm -rf

Recursively remove files

Does anyone have a solution to remove those pesky ._ and .DS_Store files that one gets after moving files from a Mac to A Linux Server?
specify a start directory and let it go? like /var/www/html/ down...
change to the directory, and use:
find . -name ".DS_Store" -print0 | xargs -0 rm -rf
find . -name "._*" -print0 | xargs -0 rm -rf
Not tested, try them without the xargs first!
You could replace the period after find, with the directory, instead of changing to the directory first.
find /dir/here ...
find /var/www/html \( -name '.DS_Store' -or -name '._*' \) -delete
Newer findutils supports -delete, so:
find . -name ".DS_Store" -delete
Add -print to also get a list of deletions.
Command will work for you if you have an up-to-date POSIX system, I believe. At least it works for me on OS X 10.8 and works for others who've tested it on macOS 10.12 (Mojave).
Credit to #ephemient in a comment on #X-Istence's post (thought it was helpful enough to warrant its own answer).
Simple command:
rm `find ./ -name '.DS_Store'` -rf
rm `find ./ -name '._'` -rf
Good luck!
cd /var/www/html && find . -name '.DS_Store' -print0 | xargs -0 rm
cd /var/www/html && find . -name '._*' -print0 | xargs -0 rm
You could switch to zsh instead of bash. This lets you use ** to match files anywhere in a directory tree:
$ rm /var/www/html/**/_* /var/www/html/**/.DS_Store
You can also combine them like this:
$ rm /var/www/html/**/(_*|.DS_Store)
Zsh has lots of other features that bash lacks, but that one alone is worth making the switch for. It is available in most (probably all) linux distros, as well as cygwin and OS X.
You can find more information on the zsh site.
find . -name "FILE-TO-FIND"-exec rm -rf {} \;
Example to delete "Thumbs.db" recursively;
find . -iname "Thumbs.db" -print0 | xargs -0 rm -rf
Validate by:
find . -iname "Thumbs.db"
This should now, not display any of the entries with "Thumbs.db", inside the current path.
It is better to see what is removing by adding -print to this answer
find /var/www/html \( -name '.DS_Store' -or -name '._*' \) -delete -print
if you have Bash 4.0++
#!/bin/bash
shopt -s globstar
for file in /var/www/html/**/.DS_Store /var/www/html/**/._
do
echo rm "$file"
done
A few things to note:
'-delete' is not recursive. So if .TemporaryItems (folder) has files in it, the command fails.
There are a lot of these pesky files created by macs:
.DS_Store
._.DS_Store
.TemporaryItems
.apdisk
This one command addresses all of them. Saves from running find over and over again for multiple matches.
find /home/foo \( -name '.DS_Store' -or -name '._.DS_Store' -or -name '._*' -or -name '.TemporaryItems' -or -name '.apdisk' \) -exec rm -rf {} \;
This also works:
sudo rm -rf 2018-03-*
here your deleting files with names of the format 2018-03-(something else)
keep it simple

Resources