Recursively change file extensions in Bash - regex

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

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 \;

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' {} \;

Find & replace recursively except for certain files

With regards to this post, how would I exclude one or more files from applying the string replacement? By using the aforementioned post as an example, I would like to be able to replace "apples" with "oranges" in all descendant files of a given directory except, say, ./fpd/font/symbol.php.
My idea was using the -regex switch in the find command but unfortunately it does not have a -v option like the grep command hence I can't negate the regex to not match the files where the replacement must occur.
I use this in my Git repository:
grep -ilr orange . | grep -v ".git" | grep -e "\\.php$" | xargs sed -i s/orange/apple/g {}
It will:
Run find and replace only in files that actually have the word to be replaced;
Not process the .git folder;
Process only .php files.
Needless to say you can include as many grep layers you want to filter the list that is being passed to xargs.
Known issues:
At least in my Windows environment it fails to open files that have spaces in the path or name. Never figured that one out. If anyone has an idea of how to fix this I would like to know.
Haven't tested this but it should work:
find . -path ./fpd/font/symbol.php -prune -o -exec sed -i 's/apple/orange/g' {} \;
You can negate with ! (or -not) combined with -name:
$ find .
.
./a
./a/b.txt
./b
./b/a.txt
$ find . -name \*a\* -print
./a
./b/a.txt
$ find . ! -name \*a\* -print
.
./a/b.txt
./b
$ find . -not -name \*a\* -print
.
./a/b.txt
./b

remove files when name does NOT contain some words

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.