2

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!

  • 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 Jan 31 '18 at 12:36
5

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.

  • 3
    Change the destination file name to "$(printf "abc_%03d.png" "$i")" to get the zero-padding, too – ilkkachu Jan 31 '18 at 12:19
  • I had been very close to this solution at one point… The zero-padding is the cherry on top! Thanks1 – NicApicella Jan 31 '18 at 12:23
3

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.

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.

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
0

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
0

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.