remove files when name does NOT contain some words - regex

I am using Linux and intend to remove some files using shell.
I have some files in my folder, some filenames contain the word "good", others don't.
For example:
ssgood.wmv
ssbad.wmv
goodboy.wmv
cuteboy.wmv
I want to remove the files that does NOT contain "good" in the name, so the remaining files are:
ssgood.wmv
goodboy.wmv
How to do that using rm in shell? I try to use
rm -f *[!good].*
but it doesn't work.
Thanks a lot!

This command should do what you you need:
ls -1 | grep -v 'good' | xargs rm -f
It will probably run faster than other commands, since it does not involve the use of a regex (which is slow, and unnecessary for such a simple operation).

With bash, you can get "negative" matching via the extglob shell option:
shopt -s extglob
rm !(*good*)

You can use find with the -not operator:
find . -not -iname "*good*" -a -not -name "." -exec rm {} \;
I've used -exec to call rm there, but I wonder if find has a built-in delete action it does, see below.
But very careful with that. Note in the above I've had to put an -a -not -name "." clause in, because otherwise it matched ., the current directory. So I'd test thoroughly with -print before putting in the -exec rm {} \; bit!
Update: Yup, I've never used it, but there is indeed a -delete action. So:
find . -not -iname "*good*" -a -not -name "." -delete
Again, be careful and double-check you're not matching more than you want to match first.

Related

How to remove all files with same extension except one with rm

I want to remove all files with .o extension except the specific example.o, how can I do that with rm?
Edit:
Environment: zsh
In zsh, you may use KSH_GLOB that works as extglob of bash:
setopt KSH_GLOB
echo rm !(example).o
Other option is to use extended_glob with a slightly different globbing syntax:
setopt extended_glob
echo rm (^example).o
Where ^ is used for negation.
Once you're satisfied with the output, remove echo before rm.
Could be something like this?
find -iname '*.o' -not -iname 'example.o' -execdir rm {} \;

linux recursive copy specified files doesn't work

I want to recursive copy all the files which start with letters in directory data to directory test. So I wrote this:
find data -type f -exec grep '^[a-z]' {} \; -exec cp -f {} ./test \;
However, it also matched other files.
What's wrong with the code?
Your command isn't executing grep on filenames, but rather on the contents of those files.
You say:
copy all the files which start with letters in directory
which would use a find command that's matching filenames which requires the -name option. For example,
find data -type f -name '[a-z]*'
By using the -exec option to find, instead you're executing the provided command (grep '^[a-z]' {}) on every file that find finds in the data directory since there is no filename matching clause (-name).
The command you likely want is:
find data -type f -name '[a-z]*' -exec cp -f {} ./test \;

Find command in shell with regular expression to find files with two extensions

I am trying to list generated log and zip files from my application server.
Files which are .log or .zip
These files include digits in their name. i.e. Files with any number of digits in their name
Files should be older than +5 days.
I used below expression. but looks something wrong. Could you please assist with regular expression?
ROOT_DIR=applications/jboss-as/servers/
find $ROOT_DIR -name '*[0-9]*[zip|log]' -mtime +5
Finally I wish to delete these files using command
find $ROOT_DIR -name '*[0-9]*[zip|log]' -mtime +5 -exec rm {} \;
The first command will find them and display.
find $ROOT_DIR ! -readable -prune -mtime +5 -type f | egrep -e "^.*\.(log|zip)$"
The second one will remove them all
find $ROOT_DIR ! -readable -prune -mtime +5 -type f | egrep -e "^.*\.(log|zip)$" | xargs -L 1 rm
You could do it this way (with most versions of find):
find "$ROOT_DIR" '(' -name '*[0-9]*.log' -o -name '*[0-9]*.zip' ')' -mtime +5 -exec rm {} +
The + is from POSIX 2008 and means "run the exec'd command with as many file names as convenient" whereas the older alternative ';' (or \;) means "run the exec'd command once per file name".
If you have GNU find, you can use various dialects of regular expression:
find "$ROOT_DIR" -regex '.*\.\(zip\|bz2\)' -mtime +5 -delete
This uses the default regex mode; you can use some alternatives to avoid using so many backslashes. The -delete option uses the unlink() system call rather than invoking an external command; it is more efficient, therefore.

Recursively change file extensions in Bash

I want to recursively iterate through a directory and change the extension of all files of a certain extension, say .t1 to .t2. What is the bash command for doing this?
Use:
find . -name "*.t1" -exec bash -c 'mv "$1" "${1%.t1}".t2' - '{}' +
If you have rename available then use one of these:
find . -name '*.t1' -exec rename .t1 .t2 {} +
find . -name "*.t1" -exec rename 's/\.t1$/.t2/' '{}' +
None of the suggested solutions worked for me on a fresh install of debian 14.
This should work on any Posix/MacOS
find ./ -depth -name "*.t1" -exec sh -c 'mv "$1" "${1%.t1}.t2"' _ {} \;
All credits to:
https://askubuntu.com/questions/35922/how-do-i-change-extension-of-multiple-files-recursively-from-the-command-line
If your version of bash supports the globstar option (version 4 or later):
shopt -s globstar
for f in **/*.t1; do
mv "$f" "${f%.t1}.t2"
done
I would do this way in bash :
for i in $(ls *.t1);
do
mv "$i" "${i%.t1}.t2"
done
EDIT :
my mistake : it's not recursive, here is my way for recursive changing filename :
for i in $(find `pwd` -name "*.t1");
do
mv "$i" "${i%.t1}.t2"
done
Or you can simply install the mmv command and do:
mmv '*.t1' '#1.t2'
Here #1 is the first glob part i.e. the * in *.t1 .
Or in pure bash stuff, a simple way would be:
for f in *.t1; do
mv "$f" "${f%.t1}.t2"
done
(i.e.: for can list files without the help of an external command such as ls or find)
HTH
My lazy copy-pasting of one of these solutions didn't work, but I already had fd-find installed, so I used that:
fd --extension t1 --exec mv {} {.}.t2
From fd's manpage, when executing a command (using --exec):
The following placeholders are substituted by a
path derived from the current search result:
{} path
{/} basename
{//} parent directory
{.} path without file extension
{/.} basename without file extension

How to use find and the prune option with an while loop

i've got an question about find, prune and print combined with an while loop. I want find every file named trace but not the files ending on mailed. Also i want to exclude the files in the lost+found directory. My idea was to use the following command:
find /opt/myTESTdir/ -iwholename '*lost+found' -prune -o -ctime +4 -type f -iname "*trace*" -not -iname "*.mailed*" -print0 | while read file ; do newfile=${file%.txt}".mailed" ; mv -v $file $newfile ; done
My question is now should this work or is there an syntax error? I've tried out the find command without everything behind the pipe and it seems, that's work correctly. But i'm not sure about the combination. I hope you could answer me :)
(Sorry for my bad english)
In while loop, it seems you are trying to rename files with extension .txt to .mailed. You can achieve the same using -exec option.
Try adding following portion to the end of your find command and remove piping to while loop.
-exec sh -c 'mv -f $0 ${0%.txt}.mailed' {} \;
Complete command would look like
find /opt/myTESTdir/ -iwholename '*lost+found' -prune -o -ctime +4 -type f -iname '*trace*' ! -iname '*.mailed*' -exec sh -c 'mv -f $0 ${0%.txt}.mailed' {} \;