22

I originally had a set of images of the form image_001.jpg, image_002.jpg, ...

I went through them and removed several. Now I'd like to rename the leftover files back to image_001.jpg, image_002.jpg, ...

Is there a Linux command that will do this neatly? I'm familiar with rename but can't see anything to order file names like this. I'm thinking that since ls *.jpg lists the files in order (with gaps), the solution would be to pass the output of that into a bash loop or something?

codeforester
  • 39,467
  • 16
  • 112
  • 140
DisgruntledGoat
  • 70,219
  • 68
  • 205
  • 290

6 Answers6

15

If I understand right, you have e.g. image_001.jpg, image_003.jpg, image_005.jpg, and you want to rename to image_001.jpg, image_002.jpg, image_003.jpg.

EDIT: This is modified to put the temp file in the current directory. As Stephan202 noted, this can make a significant difference if temp is on a different filesystem. To avoid hitting the temp file in the loop, it now goes through image*

i=1; temp=$(mktemp -p .); for file in image*
do
mv "$file" $temp;
mv $temp $(printf "image_%0.3d.jpg" $i)
i=$((i + 1))
done                                      
Matthew Flaschen
  • 278,309
  • 50
  • 514
  • 539
  • Nice (as long as mktemp creates a file on the same volume, otherwise moving to a temporary subdirectory is faster) – Stephan202 May 19 '09 at 00:55
  • I'm going to mark this as the answer, but I didn't need to use a temp directory: my images actually started at 002 (with gaps), therefore there couldn't be any conflicts. 002 is renamed 001, 005->002, 006->003 and so on. – DisgruntledGoat May 19 '09 at 23:16
  • 3
    For other people's reference, my final command was: i=0; for file in *.jpg; do mv "$file" $(printf "image_%0.3d.jpg" $i); i=$((i+1)); done – DisgruntledGoat May 19 '09 at 23:18
9

A simple loop (test with echo, execute with mv):

I=1
for F in *; do
  echo "$F" `printf image_%03d.jpg $I`
  #mv "$F" `printf image_%03d.jpg $I` 2>/dev/null || true
  I=$((I + 1))
done

(I added 2>/dev/null || true to suppress warnings about identical source and target files. If this is not to your liking, go with Matthew Flaschen's answer.)

Community
  • 1
  • 1
Stephan202
  • 59,965
  • 13
  • 127
  • 133
2

Try the following script:

numerate.sh

This code snipped should do the job:

./numerate.sh -d <your image folder> -b <start number> -L 3 -p image_ -s .jpg -o numerically -r
Paul Weibert
  • 194
  • 1
  • 4
2

Some good answers here already; but some rely on hiding errors which is not a good idea (that assumes mv will only error because of a condition that is expected - what about all the other reaons mv might error?).

Moreover, it can be done a little shorter and should be better quoted:

for file in *; do
    printf -vsequenceImage 'image_%03d.jpg' "$((++i))"
    [[ -e $sequenceImage ]] || \
        mv "$file" "$sequenceImage"
done

Also note that you shouldn't capitalize your variables in bash scripts.

Community
  • 1
  • 1
lhunath
  • 120,288
  • 16
  • 68
  • 77
1

This does the reverse of what you are asking (taking files of the form *.jpg.001 and converting them to *.001.jpg), but can easily be modified for your purpose:

for file in * 

do

if [[ "$file" =~ "(.*)\.([[:alpha:]]+)\.([[:digit:]]{3,})$" ]]

then

mv "${BASH_REMATCH[0]}" "${BASH_REMATCH[1]}.${BASH_REMATCH[3]}.${BASH_REMATCH[2]}"

fi

done
Svante
  • 50,694
  • 11
  • 78
  • 122
ennuikiller
  • 46,381
  • 14
  • 112
  • 137
0

I was going to suggest something like the above using a for loop, an iterator, cut -f1 -d "_", then mv i i.iterator. It looks like it's already covered other ways, though.

jess
  • 916
  • 1
  • 8
  • 14