Score:2

How to batch rename sequentially prefixed numbers to new sequential prefixes via terminal?

ng flag

How can I batch rename, using the terminal, a set of files where multiple numbers share the same prefix so that all those prefixes are set to new ones?

For example:

011.foo.txt   -> 001.foo.txt
011.bar.psd   -> 001.bar.psd
011.baz.gif   -> 001.baz.gif
012.qux.js    -> 002.qux.js
012.corge.png -> 002.corge.png
...
020.thud.txt  -> 010.thud.txt

I'd like to use the rename command if possible:

rename [ -h|-m|-V ] [ -v ] [ -0 ] [ -n ] [ -f ] [ -d ] [ -e|-E perlexpr]*|perlexpr [ files ]

Really appreciate your help figuring this out, Thanks!

hr flag
Do you want to simply reduce the numerical value of the prefix by 10 in each case?
user10489 avatar
in flag
The relationship between your source and target numbers is not clear. A single invocation of the rename command is not going to be able to make a complex manipulation like this. A safe approach to something like this would be to generate a file (using awk? perl?) with multiple mv commands and examine the results and edit out any problems, and the run the file.
io4250 avatar
ng flag
@steeldriver that's an interesting solution I hadn't thought of. But ideally I'd like to have the renamed files start at a specific point (eg 001 or 005) without doing the math if possible so that I can use the same solution regardless of the initial number in the current list of files.
io4250 avatar
ng flag
@user10489 Sorry, I made a typo in my example and have updated it (010.thud.txt was previously incorrectly named 003.thud.txt)
Score:2
hr flag

If you want files with the same original prefix to map to the same prefix in the new scheme without relying on subtraction, then you could do so by creating a hash (associative array) of the prefixes, numbering those sequentially, then performing the rename substitutions by lookup in the hash. Ex.

$ rename -n -E '
  BEGIN {
    my $n = 1;
    our %pfxs;
    foreach my $f (@ARGV) {
      $pfxs{$1} = (exists $pfxs{$1} ? $pfxs{$1} : $n++) if $f =~ /^(\d{3})/
    }
  }
  our %pfxs;
  s/^(\d{3})/sprintf "%03d", $pfxs{$1}/e
' [0-9][0-9][0-9].*
rename(011.bar.psd, 001.bar.psd)
rename(011.baz.gif, 001.baz.gif)
rename(011.foo.txt, 001.foo.txt)
rename(012.corge.png, 002.corge.png)
rename(012.qux.js, 002.qux.js)
rename(020.thud.txt, 003.thud.txt)

Since it doesn't rely on subtraction, this method can be used even when the original prefixes are non-numeric.

This can undoubtedly be improved - in particular, one could check the max value of $n at the end of the mapping and choose the width of the output accordingly.

io4250 avatar
ng flag
Sorry @steeldriver I made a mistake when I typed 003.thud.txt, I actually meant to type 010.thud.txt
hr flag
@io4250 tbh I assumed it didn't really matter since we don't have the `...` parts of the sequence
Score:2
cn flag

Using the perl rename as requested.

For what your question demonstrates:

rename -n 's/^(\d+)/sprintf "%03d", $1-10/e' *

dry-run output:

rename(011.bar.psd, 001.bar.psd)
rename(011.baz.gif, 001.baz.gif)
rename(011.foo.txt, 001.foo.txt)
rename(012.corge.png, 002.corge.png)
rename(012.qux.js, 002.qux.js)
rename(020.thud.txt, 010.thud.txt)

For what the question title says, with actual sequential prefixes:

rename -n -E 'use vars q{$n}' -e 's/^(\d+)/sprintf "%03d", ++$n/e' *
rename(011.bar.psd, 001.bar.psd)
rename(011.baz.gif, 002.baz.gif)
rename(011.foo.txt, 003.foo.txt)
rename(012.corge.png, 004.corge.png)
rename(012.qux.js, 005.qux.js)
rename(020.thud.txt, 006.thud.txt)

For the first one, if you don't want to hardcode the delta 10:

rename -n -E 'use vars q{$delta}' -e '
    s{^(\d+)}{
        $delta = $1 - 1 unless defined $delta;
        sprintf "%03d", $1 - $delta
    }e
' *
Score:1
jp flag

In bash

Try this (dry-run) from within the directory containing the files:

for f in *
do
    r="${f#*.}"
    p="${f%%.*}"
    if [ "$p" -eq "$p" ] 2>/dev/null
    then  
    p="$((10#$p - 10))"
    p="$(printf "%03d" $p)"
    echo mv -- "$f" "$p.$r"
    fi 
done

If you are satisfied with the printed output, then change the line:

echo mv -- "$f" "$p.$r"

to:

mv -- "$f" "$p.$r"

removing echo to do the actual renaming.

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.