2

Say I have a range of files named: "blah-10-blah", "blah-11-blah" etc...up to "blah-30-blah".

I would like to change the names to "blah-20-blah", "blah-21-blah" etc...up to "blah-40-blah".

Is there a way of doing this in the terminal?

kos
  • 35,891
Orm
  • 21

3 Answers3

2

It's important to process the files in an inverse numerical order, otherwise the task will fail due to already existing files with the target filename:

find . -maxdepth 1 -type f -name 'blah-??-blah' -print0 | sort -zr | xargs -0 rename 's/-\K([0-9]{2})/$1+10/e'
  • find . -maxdepth 1 -type f -name 'blah-??-blah' -print0: prints a NULL-separated list of the files in the current working directory matching the globbing pattern blah-??-blah;
  • sort -zr: sorts the list in an inverse numerical order;
  • xargs -0 rename 's/-\K([0-9]{2})/$1+10/e': renames the files substituting the first couple of digits after a dash with the correspondent value incremented by 10;
% tree
.
├── blah-10-blah
├── blah-11-blah
├── blah-12-blah
├── blah-13-blah
├── blah-14-blah
├── blah-15-blah
├── blah-16-blah
├── blah-17-blah
├── blah-18-blah
├── blah-19-blah
├── blah-20-blah
├── blah-21-blah
├── blah-22-blah
├── blah-23-blah
├── blah-24-blah
├── blah-25-blah
├── blah-26-blah
├── blah-27-blah
├── blah-28-blah
├── blah-29-blah
└── blah-30-blah

0 directories, 21 files
% find . -maxdepth 1 -type f -name 'blah-??-blah' -print0 | sort -zr | xargs -0 rename 's/-\K([0-9]{2})/$1+10/e'
% tree
.
├── blah-20-blah
├── blah-21-blah
├── blah-22-blah
├── blah-23-blah
├── blah-24-blah
├── blah-25-blah
├── blah-26-blah
├── blah-27-blah
├── blah-28-blah
├── blah-29-blah
├── blah-30-blah
├── blah-31-blah
├── blah-32-blah
├── blah-33-blah
├── blah-34-blah
├── blah-35-blah
├── blah-36-blah
├── blah-37-blah
├── blah-38-blah
├── blah-39-blah
└── blah-40-blah

0 directories, 21 files

If using Zsh, the task can be heavily simplified, as Zsh allows to expand filenames in an inverse numerical order:

rename 's/-\K([0-9]{2})/$1+10/e' blah-??-blah(On)
% tree
.
├── blah-10-blah
├── blah-11-blah
├── blah-12-blah
├── blah-13-blah
├── blah-14-blah
├── blah-15-blah
├── blah-16-blah
├── blah-17-blah
├── blah-18-blah
├── blah-19-blah
├── blah-20-blah
├── blah-21-blah
├── blah-22-blah
├── blah-23-blah
├── blah-24-blah
├── blah-25-blah
├── blah-26-blah
├── blah-27-blah
├── blah-28-blah
├── blah-29-blah
└── blah-30-blah

0 directories, 21 files
% rename 's/-\K([0-9]{2})/$1+10/e' blah-??-blah(On)
% tree                                
.
├── blah-20-blah
├── blah-21-blah
├── blah-22-blah
├── blah-23-blah
├── blah-24-blah
├── blah-25-blah
├── blah-26-blah
├── blah-27-blah
├── blah-28-blah
├── blah-29-blah
├── blah-30-blah
├── blah-31-blah
├── blah-32-blah
├── blah-33-blah
├── blah-34-blah
├── blah-35-blah
├── blah-36-blah
├── blah-37-blah
├── blah-38-blah
├── blah-39-blah
└── blah-40-blah

0 directories, 21 files
kos
  • 35,891
  • 1
    I think you are referring to this one: http://askubuntu.com/a/674732/72216 which you deleted :) (I also remembered). – Jacob Vlijm Oct 31 '15 at 21:40
  • 1
    @JacobVlijm Indeed, that's why I couldn't find it! And I have no idea about how you could find it either :). However as always with these questions, I'm not sure if voting to close or not. They're so similar but this answer won't work for the other question, and the answer in the other question won't work for this question. – kos Oct 31 '15 at 21:48
  • I usually switch to Google in these cases, I knew it was something with "add" and "number". "add number to name askubuntu" did the job (second hit) :) Although the category is well represented, the exact question seems no dupe. – Jacob Vlijm Oct 31 '15 at 21:53
  • 1
    @JacobVlijm Right :). Yes, closing as duplicate would be wrong. Ok, let's just let this other almost-duplicate question survive. – kos Oct 31 '15 at 21:59
  • Excellent idea kos..i also had similar idea..gonna put something similar.. – heemayl Nov 01 '15 at 03:08
  • Wow, very interesting. Thank you muchly! Small additional question: In the folder there are also other files with the same name structure (but other numbers). Can I set a condition so that it only performs the operation on files with numbers within a certain range? – Orm Nov 01 '15 at 11:38
  • @Orm Yes you could, but you should point out exactly which numbers if you want a precise answer, as specifying ranges is often tricky. In general you can specify ranges in globbing patterns using [N-M]; i.e. if you wanted to match only files ranging from 5 to 9, you'd change the globbing pattern in find to blah-[5-9]-blah – kos Nov 01 '15 at 15:36
  • @Orm But mainly the problem is that you can only specify single digits using that method. and that you can't e.g. match files ranging from "blah-15-blah" to "blah-29-blah" at all, as globbing pattern don't allow alternation. If that's the case you should change the regular expression in the rename command e.g. to s/-\K(1[5-9]|2[0-9])/$1+10/e. What are you after exactly? – kos Nov 01 '15 at 16:03
  • Oh ok, I see. I had an audiobook of about 100 mp3s, but they were not in the right order. 'blah-10-blah' to 'blah-30-blah' should have been 'blah-70-blah' to 'blah-90-blah' and vice versa. So I wanted to do the operation only on the files that had the wrong names. – Orm Nov 01 '15 at 19:04
  • @Orm Ah ok, then this should do: find . -maxdepth 1 -type f -name 'blah-??-blah' -print0 | sort -zr | xargs -0 rename 's/-\K([7-8][0-9]|90)/$1+10/e' – kos Nov 01 '15 at 20:27
2

You can do:

#!/bin/bash
files=( blah-??-blah )
for ((i=${#files[@]}-1; i>=0; i--)); do

    first="${files[$i]%%-*}" 
    num="$(grep -o '[0-9]\+' <<<"${files[$i]}")" 
    last="${files##*-}"

        echo mv "$first-$num-$last" "$first-$((num+10))-$last" 
done

If you are satisfied with everything, add | bash at the end to let the mv operation take place.

  • We have put the relevant file names into an array files

  • Then we have iterated over the elements of the array from the end i.e. from last to first

  • first will have the first part of file name i.e. part prior to the first -

  • last will have the last part of the file name i.e. the part after last -

  • num will have the number in between two -

  • mv "$first-$num-$last" "$first-$((num+10))-$last" will do the rename operation accordingly

Example:

$ ls -1
blah-10-blah
blah-11-blah
blah-12-blah
blah-13-blah
blah-14-blah
blah-15-blah
blah-16-blah
blah-17-blah
blah-18-blah
blah-19-blah
blah-20-blah
blah-21-blah
blah-22-blah
blah-23-blah
blah-24-blah
blah-25-blah
blah-26-blah
blah-27-blah
blah-28-blah
blah-29-blah
blah-30-blah
blah-foo-1
blah-foo-2

$ for ((i=${#files[@]}-1; i>=0; i--)); do first="${files[$i]%%-*}" \
    num="$(grep -o '[0-9]\+' <<<"${files[$i]}")" last="${files##*-}"; \ 
      echo mv "$first-$num-$last" "$first-$((num+10))-$last"; done

mv blah-30-blah blah-40-blah
mv blah-29-blah blah-39-blah
mv blah-28-blah blah-38-blah
mv blah-27-blah blah-37-blah
mv blah-26-blah blah-36-blah
mv blah-25-blah blah-35-blah
mv blah-24-blah blah-34-blah
mv blah-23-blah blah-33-blah
mv blah-22-blah blah-32-blah
mv blah-21-blah blah-31-blah
mv blah-20-blah blah-30-blah
mv blah-19-blah blah-29-blah
mv blah-18-blah blah-28-blah
mv blah-17-blah blah-27-blah
mv blah-16-blah blah-26-blah
mv blah-15-blah blah-25-blah
mv blah-14-blah blah-24-blah
mv blah-13-blah blah-23-blah
mv blah-12-blah blah-22-blah
mv blah-11-blah blah-21-blah
mv blah-10-blah blah-20-blah

$ for ((i=${#files[@]}-1; i>=0; i--)); do first="${files[$i]%%-*}" \
    num="$(grep -o '[0-9]\+' <<<"${files[$i]}")" last="${files##*-}";\ 
      echo mv "$first-$num-$last" "$first-$((num+10))-$last"; done | bash

$ ls -1
blah-20-blah
blah-21-blah
blah-22-blah
blah-23-blah
blah-24-blah
blah-25-blah
blah-26-blah
blah-27-blah
blah-28-blah
blah-29-blah
blah-30-blah
blah-31-blah
blah-32-blah
blah-33-blah
blah-34-blah
blah-35-blah
blah-36-blah
blah-37-blah
blah-38-blah
blah-39-blah
blah-40-blah
blah-foo-1
blah-foo-2
heemayl
  • 91,753
0

Basic idea of this approach is to throw all the files into temporary "basket" directory, and then pick them out one by one, create new name , and move back with new name into the original directory.

The script bellow takes a single argument ( $1 ), which is directory where the files you want to rename are located.

Demo

xieerqi:$ ls testdir                                                                                         
blah-10-blah  blah-20-blah  blah-30-blah  blah-40-blah

xieerqi:$ cat testdir/*                                                                                      
I am file 10 
I am file 20 
I am file 30 
I am file 40 

xieerqi:$ ./incrementNames.sh testdir                                                                        
blah-10-blah ../blah-20-blah
blah-20-blah ../blah-30-blah
blah-30-blah ../blah-40-blah
blah-40-blah ../blah-50-blah

xieerqi:$ ls testdir
blah-20-blah  blah-30-blah  blah-40-blah  blah-50-blah  TMP/

xieerqi:$ cat testdir/blah                                                                                   
blah-20-blah blah-30-blah blah-40-blah blah-50-blah 
xieerqi:$ cat testdir/blah-20-blah                                                                           
I am file 10 

xieerqi:$ cat testdir/blah-30-blah                                                                           
I am file 20 

Script

#!/bin/bash

if [ "$#" -ne 1 ]; then
    echo "Usage: incrementNames.sh /path/to/dir" && exit
fi

# navigate to target directory
# create temporary directory
cd "$1"
mkdir TMP
# move everything to TMP directory
find . -maxdepth 1 -type f -iname "*-*" -exec mv -t TMP {} \+
# drop down to TMP directory pick filesback into the directory , and rename them as we go
cd TMP
find .  -maxdepth 1 -type f -iname "*-*" -printf "%f\n"  | sort  | while IFS= read FILENAME
do
        NEW="$( awk -F '-'  '{print $1FS$2+10FS$3 }' <<< "$FILENAME")"
        echo "$FILENAME" "../$NEW" 
        mv "$FILENAME" ../"$NEW"
done

Limitation

This script is specifically for files following pattern text-number-text, or at least text-number patter. It wont work for others

Sergiy Kolodyazhnyy
  • 105,154
  • 20
  • 279
  • 497
  • 3
    This seems ok for few small files, but if there are hundreds of files or the overall size is in the GB range, it may take large amounts of time because the files will be copied twice (only temporarily, so no additional disk space is used afterwards, but it takes time) – Byte Commander Oct 31 '15 at 23:11