#!/bin/bash
#
name=$1
type=$2
number=1
for file in ./**
do
if [ $number -lt 10 ]
then
filenumber=00$number
elif [ $number -lt 100 ]
then
filenumber=0$number
fi
tempname="$name""$filenumber"."$type"
if [ -f $file ]
then
if [ ! -x $file ]
then
mv $file $tempname
fi
fi
((number++))
done
So the purpose of this code is to rename files in a directory to a new name, number, and filetype. However anything that has already been renamed in my folder gets deleted. Like if I have:
file001.jpg, file002.jpg, file003.jpg, file004.jpg
newfile1, newfile2, newfile3, newfile4
when I run: ./renumber file jpg
I end up with:
file005.jpg, file006.jpg, file007.jpg, file008.jpg
and my original 4 are gone. What happened?
ALSO!
How can I get the program to also do the same to files in sub directories?
This would take some work to keep them in subdirs. I see you already use the globstar ./**
for recursion but didn't include the shopt -s globstar
This is bash4 feature, so let's write bash code and use all of it's nice features:
#!/bin/bash
(( $# == 2 )) || {
printf 'Usage: %s <prefix> <ext>\n' "$0"
exit 1
}
prefix=$1
type=$2
number=1
shopt -s globstar
for file in ./**; do
[ -f "$file" ] || continue # only "regular" files
printf -v new '%s%03d.%s' "$prefix" $((number++)) "$type"
# skip to next file if name conflict
[ -f "$new" ] && continue
[ -x "$file" ] && continue
mv "$file" "$new"
echo "$file -> $new"
done
mute@thedoctor:~/temp/TheGreatGizmo$ touch IMG_{0000..0005}.jpg more/IMG_{000..0005}.jpg
mute@thedoctor:~/temp/TheGreatGizmo$ ls
IMG_0000.jpg IMG_0001.jpg IMG_0002.jpg IMG_0003.jpg IMG_0004.jpg IMG_0005.jpg more script
mute@thedoctor:~/temp/TheGreatGizmo$ ./script
Usage: ./script <prefix> <ext>
mute@thedoctor:~/temp/TheGreatGizmo$ ./script file jpg
./IMG_0000.jpg -> file001.jpg
./IMG_0001.jpg -> file002.jpg
./IMG_0002.jpg -> file003.jpg
./IMG_0003.jpg -> file004.jpg
./IMG_0004.jpg -> file005.jpg
./IMG_0005.jpg -> file006.jpg
./more/IMG_0000.jpg -> file007.jpg
./more/IMG_0001.jpg -> file008.jpg
./more/IMG_0002.jpg -> file009.jpg
./more/IMG_0003.jpg -> file010.jpg
./more/IMG_0004.jpg -> file011.jpg
./more/IMG_0005.jpg -> file012.jpg
mute@thedoctor:~/temp/TheGreatGizmo$ ls .
file001.jpg file003.jpg file005.jpg file007.jpg file009.jpg file011.jpg more
file002.jpg file004.jpg file006.jpg file008.jpg file010.jpg file012.jpg script
2 Likes
Could you avoid bash4 (and globstar) requirement by replacing:
shopt -s globstar
for file in ./**; do
[ -f "$file" ] || continue # only "regular" files
with
find . -type f -print | while read file; do
1 Like
Sorry, I'm really new to bash. Could someone explain how each part of these code segments work in 3 year old terms? And what the second one does? I like to understand what is going on. These were taken from Neutronscott's reply. Thanks
EDIT: Okay, I see what you did here. Though I'm not sure what the "$0" is for on the end of the printf statement.
(( $# == 2 )) || {
printf 'Usage: %s <prefix> <ext>\n' "$0"
exit 1
}
shopt -s globstar
continue # only "regular" files
EDIT: and I understand everything here other than '%s%03d.%s'
printf -v new '%s%03d.%s' "$prefix" $((number++)) "$type"
# skip to next file if name conflict
[ -f "$new" ] && continue
[ -x "$file" ] && continue
continue just resumes at the next iteration of the enclosing for loop. In this particular code that means stop processing the current file and start processing the next file.
The current code is less than ideal as it will rename existing renamed files and skip files.
see below:
$ touch abc_{0000..0003} more/img_{0000..0003} file{000..002}.jpg
$ ls
abc_0000 abc_0001 abc_0002 abc_0003 file000.jpg file001.jpg file002.jpg more script
$ ./script file jpg
./abc_0002 -> file003.jpg
./abc_0003 -> file004.jpg
./file000.jpg -> file005.jpg
./file001.jpg -> file006.jpg
./file002.jpg -> file007.jpg
./more/img_0000 -> file008.jpg
./more/img_0001 -> file009.jpg
./more/img_0002 -> file010.jpg
./more/img_0003 -> file011.jpg
$ ls
abc_0000 file003.jpg file005.jpg file007.jpg file009.jpg file011.jpg script
abc_0001 file004.jpg file006.jpg file008.jpg file010.jpg more
This slight update should be a little safer.
(( $# == 2 )) || {
printf 'Usage: %s <prefix> <ext>\n' "$0"
exit 1
}
prefix=$1
type=$2
number=1
find . -type f -print | while read file; do
# skip already processed files
[[ "$file" = ./$prefix[0-9][0-9][0-9].$type ]] && continue
# skip executable files (eg this script)
[ -x "$file" ] && continue
# find next unused sequence
while true
do
printf -v new '%s%03d.%s' "$prefix" $((++number)) "$type"
[ -f "$new" ] || break
done
mv "$file" "$new"
echo "$file -> $new"
done
1 Like