4

I'm trying to renumber a list of files, which at a given point in their filename have one index consisting of a four-digit number. These files are consistently output starting from 0001, but need to start at 1001.

  • ./file_name.0001.jpg should be ./file_name.1001.jpg
  • ./file_name.0734.jpg should be ./file_name.1734.jpg
  • etc.

I've come across rename, but I'm not familiar with Perl. Improvising on examples, I believed that this would work:

rename -n 's/\d{4}/our $i //= 1001; ++$i/e' -- *.jpg

Unfortunately, by the time of the increment, it already seems to have forgotten about $i:

Global symbol "$i" requires explicit package name (did you forget to declare "my $i"?) at (user-supplied code).

There's no safety either for files that get renamed to a name that already exists, but I have no idea where that would go.

2
  • @StéphaneChazelas wow, thanks so much! Perl is a mystery to me. Any chance you'd know how to prevent name clashing? Preferably within this one-liner, but it's not strictly necessary.
    – sam_vh
    Oct 11, 2022 at 6:21
  • rename won't overwrite an existing file. To do the name clashing check before starting the renaming, I'd use zsh's zmv instead of rename Oct 11, 2022 at 6:49

1 Answer 1

5

Here, it seems you should be able to just add 1000 to the number:

rename -n -- 's/\d{4}/$& + 1000/e' *.jpg

Note that \d{4} will match the 1234 in 1234567. To match on sequences of 4 digits and no more, you'd need to check for a \d{4} not surrounded by other digits, so:

rename -n -- 's/(?<!\d)\d{4}(?!\d)/$& + 1000/e' *.jpg

Specifically, your problem was that you were using / as the s separator and using / inside the replacement. When replacement involves code, I like to s{pattern}{replacement}flags instead of s/pattern/replacement/flags. But you could always use a different character like s:pattern:replacement:flags

rename -n -- 's{\d{4}}{our $i //= 1000; ++$i}e' *.jpg

A possibly better alternative to rename is to use zsh's zmv which has the advantage of doing sanity checks before doing any renaming:

autoload -Uz zmv
i=1000; zmv -n '(*)[0-9](#c4)(*.jpg)' '$1$((++i))$2'

Or to look for 4-and-no-more digits:

i=1000; zmv -n '(|*[^0-9])[0-9](#c4)(([^0-9]*|).jpg)' '$1$((++i))$2'

Or to add 1000 to the number:

zmv -n '(*)([0-9](#c4))(*.jpg)' '$1$(($2 + 1000))$3'

See also <0-999> to match on sequences of digits that represent numbers from 0 to 999 (including 1, 01, 0001, 000000001...). (<0-999>~^[0-9](#c4)) would match on numbers 0 to 999 expressed on 4 digits.

1
  • Thanks so much, that's super helpful and very thorough. Since neither rename nor zmv will overwrite existing files, perhaps it's best to start at the end and work all the way to the front? I'm thinking along the lines of '(*)[0-9](#c4)(*.jpg)(#qOn)', would that be the right way of thinking? I'll try it out tomorrow evening.
    – sam_vh
    Oct 12, 2022 at 21:49

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .