Batch rename files to a sequential numbering

15,905

Solution 1

I can't think of a solution that handles incrementing the counter in a more clever way, but this should work:

i=0
for fi in abc_??????.png; do
    mv "$fi" abc_$i.png
    i=$((i+1))
done

It should be safe to use abc_*.png because it is expanded before the first mv is ever executed, but it can be useful to be very specific in that you only want files with a six-character timestamp at the end.

Solution 2

With rename utility as part of Perl packages, you would do:

rename -n 'our $i; s/_.*/sprintf("_%03d.png", $i++)/e' *.png

Note: -n is for dry run, remove it to rename apply on files.

Solution 3

With zsh:

typeset -A count
incr='++count[$1/$2]'
(zmv -n '([^0-9]##)<->(*)(#qn)' '$1${(l:3::0:)$((incr))}$2')

Remove the -n when happy.

(note the extra step using the incr variable to avoid the ACE vulnerability)

Example:

$ ls
a1b.png  abc_128390.png  abc_159084.png  x12y.png
a2b.png  abc_138493.png  a.png           x2y.png
$ typeset -A count
$ incr='++count[$1/$2]'
$ (zmv -n '([^0-9]##)<->(*)(#qn)' '$1${(l:3::0:)$((incr))}$2')
mv -- a1b.png a001b.png
mv -- a2b.png a002b.png
mv -- abc_128390.png abc_001.png
mv -- abc_138493.png abc_002.png
mv -- abc_159084.png abc_003.png
mv -- x2y.png x001y.png
mv -- x12y.png x002y.png

Solution 4

Avoid overwriting existing files:

i=1
for fi in abc_??????.png; do
    a="abc_$(printf '%04d' "$i").png"
    if [[ -e $a ]]; then
        echo "file $a exist, not moving $fi"
    else
        mv "$fi" "$a"
    fi
    i=$((i+1))
done

Solution 5

you can use cut to cut out parts of the filename, for example, if you want to rename files like

"1 first.jpg"
"2 second.jpg"
...

to

"10 first.jpg"
"20 second.jpg"
...

you can use

for i in *jpg; do
  mv -iv "$i" "$(echo "$i"|cut -d\  -f1)0 $(echo "$i"|cut -d\  -f2-99)";
done
Share:
15,905
NicApicella
Author by

NicApicella

Updated on September 18, 2022

Comments

  • NicApicella
    NicApicella almost 2 years

    I am trying to batch-rename a bunch of files in my shell, and even though there is plenty of material about it on the internet, I cannot seem to find a solution for my specific case.

    I have a bunch of files that have (what appears to be) a "timestamp-id":

    abc_128390.png
    abc_138493.png
    abc_159084.png
    ...
    

    that I'd like to exchange for a counter:

    abc_001.png
    abc_002.png
    abc_003.png
    ...
    

    My (plenty) naïve approach would be something like:

    mv abc_*.png abc_{001..123}.png

    Also, I could not figure out a way to make it work with a for-loop.

    FWIW, unfortunately rename is not available on this particular system.

    Any advice would be greatly appreciated!

    • ilkkachu
      ilkkachu over 6 years
      FWIW: abc_*.png abc_{001..123}.png expands to the existing file names, and then the generated names in sequence, and mv has no way to determine what the distinction between them is. (Try e.g. echo abc_*.png abc_{001..123}.png)
  • ilkkachu
    ilkkachu over 6 years
    Change the destination file name to "$(printf "abc_%03d.png" "$i")" to get the zero-padding, too
  • NicApicella
    NicApicella over 6 years
    I had been very close to this solution at one point… The zero-padding is the cherry on top! Thanks1
  • teracow
    teracow over 4 years
    Minor point: the line that increments $i can be reduced to just ((i++))
  • Merlin
    Merlin over 4 years
    i like this approach, but I think your delimiter choice is causing the whole filename to be appended. Maybe use the -c1 and -c2-99 flags for character instead of field? Prob -c2- would capture to end of line.