Bash: recursively copy and rename files - shell

I have a lot of files whose names end with '_100.jpg'. They spread in nested folder / sub-folders. Now I want a trick to recursively copy and rename all of them to have a suffix of '_crop.jpg'. Unfortunately I'm not familiar with bash scripting so don't know the exact way to do this thing. I googled and tried the 'find' command with the '-exec' para but with no luck.
Plz help me. Thanks.

find bar -iname "*_100.jpg" -printf 'mv %p %p\n' \
| sed 's/_100\.jpg$/_crop\.jpg/' \
| while read l; do eval $l; done

if you have bash 4
shopt -s globstar
for file in **/*_100.jpg; do
echo mv "$file" "${file/_100.jpg/_crop.jpg}"
one
or using find
find . -type f -iname "*_100.jpg" | while read -r FILE
do
echo mv "${FILE}" "${FILE/_100.jpg/_crop.jpg}"
done

This uses a Perl script that you may have already on your system. It's sometimes called prename instead of rename:
find /dir/to/start -type f -iname "*_100.jpg" -exec rename 's/_100/_crop' {} \;
You can make the regexes more robust if you need to protect filenames that have "_100" repeated or in parts of the name you don't want changed.

Related

Rename all files in directory and subdirectory

How do I rename files in directory and subdirectory?
I found this program, but I need to go change files in subdirectory.
for file in *#me01
do
mv "$file" "${file/#me01/_me01}"
done
n#me01
to
n_me01
The following one-liner will likely work for you:
find . -type f -name '*#me01' -execdir rename '#me01' '_me01' {} \;
The following form is likely more correct as it will change only the last # to _ if there are multiple occurrences of #me01 in the file:
for f0 in $(find . -type f -name '*#me01')
do
f1=$(printf '%s' "$f0" | sed 's/#me01$/_me01/')
mv "$f0" "$f1"
done
This latter form is also more flexible and can be built upon more easily as the regex language in sed is much more powerful than rename expressions.
If rename of directories is also required the following can easily be added...
Either:
find . -type d -name '*#me01' -execdir rename '#me01' '_me01' {} \;
Or:
for d0 in $(find . -type d -name '*#me01')
do
d1=$(printf '%s' "$d0" | sed 's/#me01$/_me01/')
mv "$d0" "$d1"
done
Using bash:
shopt -s globstar
for name in **/*#me01; do
mv "$name" "${name%#me01}_me01"
done
This enables the globstar shell option in bash which makes ** match across path separators in pathnames.
It also uses a standard parameter substitution to delete the #me01 portion at the very end of the found pathname and replace it with _me01.

Rename files in several subdirectories

I want to rename a file present in several subdirectories using bash script.
my files are in folders:
./FolderA/ABCD/ABCD_Something.ctl
./FolderA/EFGH/EFGH_Something.ctl
./FolderA/WXYZ/WXYZ_Something.ctl
I want to rename all of the .ctl file with the same name (name.ctl).
I tried several command using mv or rename but didnt work.
Working from FolderA:
find . -name '*.ctl' -exec rename *.ctl name.ctl '{}' \;
or
for f in ./*/*.ctl; do mv "$f" "${f/*.ctl/name .ctl}"; done
or
for f in $(find . -type f -name '*.ctl'); do mv $f $(echo "$f" | sed 's/*.ctl/name.ctl/'); done
Can you help me using bash?
thanks
You can do this with one line with:
find . -name *.ctl -exec sh -c 'mv "$1" `dirname "$1"`/name.ctl' x {} \;
The x just allows the filename to be positional character 1 rather than 0 which (in my opinion) wrong to use as a parameter.
Try this:
find . -name '*.ctl' | while read f; do
dn=$(dirname "${f}")
# remove the echo after you sanity check the output
echo mv "${f}" "${dn}/name.ctl"
done
find should get all the files you want, dirname will get just the directory name, and mv will perform the rename. You can remove the quotes if you're sure that you'll never have spaces in the names.

Bash. When I find a file using files=`find...`, then use a for loop "for file in $files". How do I access the path to the found file?

files=`find C:/PATH/TO/DIRECTORY -name *.txt`
for file in $files; do
#need code to rename $file, by moving it into the same directory
eg. $file was found in C:/PATH/TO/DIRECTORY/2014-05-08.
how do I rename $file to back to that directory and not to C:/PATH/TO/DIRECTORY?
You can use -execdir option in find:
find C:/PATH/TO/DIRECTORY -name '*.txt' -execdir mv '{}' '{}'-new \;
As per man find:
-execdir utility [argument ...] ;
The -execdir primary is identical to the -exec primary with the exception that utility will be executed
from the directory that holds the current file.
You would be better served by using this structure:
while read fname
do
....
done < <(find ...)
Or, if you're not using bash:
find ... | while read fname
do
....
done
The problem with storing the output of find in a variable, or even doing for fname in $(find ...), is with word splitting on whitespace. The above structures still fail if you have a file name with a newline in it, since they assume that you have one file name per line, but they're better than what you have now.
An even better solution would be something like this:
find ... -print0 | xargs -0 -n1 -Ixxx somescript.sh "xxx"
But even that might have issues if filenames have quotes or other things in them.
The bottom line is that parsing arbitrary data (which filenames can be) is hard...
find C:/PATH/TO/DIRECTORY -name \*.txt | while read file
do
dir=$(dirname "$file")
base=$(basename "$file")
mv "$file" "$dir/new_file_name"
done

Looping through all files in a given directory [duplicate]

This question already has answers here:
Looping through all files in a directory [duplicate]
(6 answers)
Closed 4 years ago.
Here is what I'm trying to do:
Give a parameter to a shell script that will run a task on all files of jpg, bmp, tif extension.
Eg: ./doProcess /media/repo/user1/dir5
and all jpg, bmp, tif files in that directory will have a certain task run on them.
What I have now is:
for f in *
do
imagejob "$f" "output/${f%.output}" ;
done
I need help with the for loop to restrict the file types and also have some way of starting under a specified directory instead of current directory.
Use shell expansion rather than ls
for file in *.{jpg,bmp,tif}
do
imagejob "$file" "output/${file%.output}"
done
also if you have bash 4.0+, you can use globstar
shopt -s globstar
shopt -s nullglob
shopt -s nocaseglob
for file in **/*.{jpg,bmp,tif}
do
# do something with $file
done
for i in `ls $1/*.jpg $1/*.bmp $1/*.tif`; do
imagejob "$i";
done
This is assuming you're using a bashlike shell where $1 is the first argument given to it.
You could also do:
find "$1" -iname "*.jpg" -or -iname "*.bmp" -or -iname "*.tif" \
-exec imagejob \{\} \;
You could use a construct with backticks and ls (or any other commando of course):
for f in `ls *.jpg *.bmp *.tif`; do ...; done
The other solutions here are either Bash-only or recommend the use of ls in spite of it being a common and well-documented antipattern. Here is how to solve this in POSIX sh without ls:
for file in *.jpg *.bmp *.tif; do
... stuff with "$file"
done
If you have a very large number of files, perhaps you also want to look into
find . -maxdepth -type f \( \
-name '*.jpg' -o -name '*.bmp' -o -name '*.tif' \) \
-exec stuff with {} +
which avoids the overhead of sorting the file names alphabetically. The -maxdepth 1 says to not recurse into subdirectories; obviously, take it out or modify it if you do want to recurse into subdirectories.
The -exec ... + predicate of find is a relatively new introduction; if your find is too old, you might want to use -exec ... \; or replace the -exec stuff with {} + with
find ... -print0 |
xargs -r0 stuff with
where however again the -print0 option and the corresponding -0 option for xargs are a GNU extension.

Shell script traversing the all subdirectories and modifying the content of files

I need to modify a number of files inside a directory. I need to modify all those files which contain particular text and have to replace with some new text.
So I thought of writing a shell script which will traverse through all the subdirectories and modify the content but I'm having problem while traversing the all possible directories.
You can use find to traverse through subdirectories looking for files and then pass them on to sed to search and replace for text.
e.g.
find /some/directory -type f -name "*.txt" -print -exec sed -i 's/foo/bar/g' {} \;
will find all txt files and replace foo with bar in them.
The -i makes sed change the files in-place. You can also supply a backup-suffix to sed if you want the files backed up before being changed.
GNU find
find /some_path -type f -name "*.txt" -exec sed -i 's/foo/bar/g' "{}" +;
http://git.savannah.gnu.org/cgit/bash.git/tree/examples/functions/recurse
:)
You want find.
for n in $(find | grep txt$)
do
echo $n
modify_content.sh $n
done

Resources