Reputation: 6826
running the following script to rename all .jpg files in the current folder works well sometimes, but it often deletes one or more files it is renaming. How would I write a script to rename files without deleting them in the process? This is running on Mac OSX 10.8 using GNU bash, version 3.2.48
this is an example file listing I would change for illustration:
original files
red.jpg
blue.jpg
green.jpg
renamed files if counter is set to 5
file_5.jpg
file_6.jpg
file_7.jpg
instead I get usually lose one or more files
#!/bin/bash
counter=5
for file in *.jpg; do
echo renaming "$file" to "file_${counter}.jpg";
mv "$file" "file_${counter}.jpg";
let "counter+=1";
done
it no longer seems to be deleting files, but the output is still not as expected. for example:
file_3.jpg
file_4.jpg
turns into
file_3.jpg
file_5.jpg
when counter is set to 4, when the expected output is
file_4.jpg
file_5.jpg
-
#!/bin/bash
counter=3
for file in *.jpg; do
if [[ -e file_${counter}.jpg ]] ; then
echo Skipping "$file", file exists.
else
echo renaming "$file" to "file_${counter}.jpg"
mv "$file" "file_${counter}.jpg"
fi
let "counter+=1"
done
Upvotes: 1
Views: 1904
Reputation: 75568
Another way is to continue your counting until a file does not exist:
#!/bin/bash
counter=1
shopt -s extglob
for file in *.jpg; do
[[ $file == ?(*/)file_+([[:digit:]]).jpg ]] && continue
until
newname=file_$(( counter++ )).jpg
[[ ! -e $newname ]]
do
continue
done
echo "renaming $file to $newname.";
mv -i "$file" "$newname" ## Remove the -i option if you think it's safe already.
done
When doing things recursively:
#!/bin/bash
shopt -s extglob
counter=1
while read file; do
dirprefix=${file%%+([^/])
until
newfile=$dirprefix/file_$(( counter++ )).jpg
[[ ! -e $newfile ]]
do
continue
done
echo "renaming $file to $newfile."
mv -i "$file" "$newfile" ## Remove the -i option if you think it's safe already.
done < <(find -type f -name '*.jpg' -and -not -regex '^.*/file_[0-9]\+$')
Upvotes: 1
Reputation: 4399
I think you are glazing over an obvious problem with the glob. If the glob matches file_2.jpg, it will try and create file_file_2.jpg (I don't mean that in the literal sense, just that you will be reprocessing files you already processed). To solve this, you need to make sure your initial glob expression doesn't match the files you have already moved:
shopt -s extglob
i=0
for f in !(file_*).jpg ; do
while [[ -e "file_${i}.jpg" ]] ; do
(( i++ ))
done
mv -v "$f" "file_$i.jpg"
(( i++ ))
done
Upvotes: 2
Reputation: 785856
Instead of iterating over *.jpg
you should skip your already renamed files i.e. file_[0-9]*.jpg
and run your loop like this:
counter=5
while read file; do
echo renaming "$file" to "file_${counter}.jpg";
mv -n "$file" "file_${counter}.jpg";
let "counter+=1";
done < <(find . -maxdepth 1 -name "*.jpg" -not -name "file_[0-9]*.jpg")
Upvotes: 1
Reputation: 4454
What choroba said is correct. You can also use:
mv "$file" "file_${counter}.jpg" -n
to simply neglect the move when the destination filename already exists, or
mv "$file" "file_${counter}.jpg" -i
to ask whether it should overwrite or not.
Upvotes: 1
Reputation: 242123
The problem is that some of the files already have names corresponding to the target names. For example, if there are files
file_1.jpg
file_7.jpg
and you start with counter=7
, you overwrite file_7.jpg
with file_1.jpg
in the first step, and then rename it to file_8.jpg
.
You can use mv -n
to prevent clobbering (if supported), or test for existence before running the command
if [[ -e file_${counter}.jpg ]] ; then
echo Skipping "$file", file exists.
else
mv "$file" "file_${counter}.jpg"
fi
Upvotes: 10