user4369032
user4369032

Reputation:

Batch rename multiple numbers in filename with different padding

I am trying to rename a batch of files of the form:

test1_run1
test1_run2
   ...
test1_run10
  ...
test10_run1
test10_run2
  ...
test10_run10

to the form with multiple paddings. For the first number I need padding with zeros of length 5 and for the second with length 3.

The final result should be of the form:

test00001_run001
test00001_run002
  ...
test00001_run010
  ...
test00010_run001
test00010_run002
  ...
test00010_run010

How can I do this in bash for all the files in a particular folder?

Upvotes: 0

Views: 213

Answers (3)

gniourf_gniourf
gniourf_gniourf

Reputation: 46823

In bash:

#!/bin/bash

shopt -s nullglob extglob

for file in test+([[:digit:]])_run+([[:digit:]]); do
    [[ $file =~ ^test([[:digit:]]+)_run([[:digit:]]+)$ ]]
    printf -v newfile 'test_%05d_run%03d' "$((10#${BASH_REMATCH[1]}))" "$((10#${BASH_REMATCH[2]}))"
    echo mv "$file" "$newfile"
done

Run this from within the folder you want to process. This will only echo the mv commands to be performed. Remove the echo if you're happy with the result.

  • we're using the shell option nullglob so that non-matching globs expand to nothing;
  • we're using the shell option extglob because the for loop will use extended globs;
  • the extended glob test+([[:digit:]])_run+([[:digit:]]) will expand to the files matching this pattern (if any)
  • we're using a regex to get the digits from the file names; the first number will be in BASH_REMATCH[1] and the second in BASH_REMATCH[2].
  • we're using printf to format the new file name; the modifiers %05d and %03d will format the numbers according to your wishes (with appropriate leading zeroes). Observe that we're using ((10#${BASH_REMATCH[1]})) to explicitly specify that the number is in radix 10, in case you have a file test09_run001. The 09 part would make bash misinterpret the number in radix 8 (because of the leading 0) and you'll get a complaint; the -v switch tells printf to not output on standard output, but to store the output in variable newfile;
  • finally we perform the mv.

Upvotes: 1

Diego Sevilla
Diego Sevilla

Reputation: 29021

If you don't want to use perl or awk, and strictly use bash and some utility programs that are available in most distribution, you can try something like this:

for i in * ; do
    testpart=`echo $i | cut -d_ -f1`
    testnum=${testpart#test}
    runpart=`echo $i | cut -d_ -f2`
    runnum=${runpart#run}
    destfile=test`printf %05d $testnum`_run`printf %03d $runnum`

    mv $i $destfile
done

Upvotes: 1

fedorqui
fedorqui

Reputation: 289605

We can convert the string into test + 5 digits + _run + 3 digits formats by saying:

$ awk -F"test" '{split($2,a,"_run"); printf "%s%0.5d%s%0.3d\n", FS, a[1], "_run", a[2]}' a
test00001_run001
test00001_run002
test00001_run010
test00010_run001
test00010_run002
test00010_run010

This works by using test as field separator and splitting the 2nd field in two parts: before and after _run. Then, it uses the printf thingies to get the proper output.

Then, you can print mv together with the previous value and say:

$ awk -F"test" '{split($2,a,"_run"); printf "mv %s %s%0.5d%s%0.3d\n", $0, FS, a[1], "_run", a[2]}' a
mv test1_run1 test00001_run001
mv test1_run2 test00001_run002
mv test1_run10 test00001_run010
mv test10_run1 test00010_run001
mv test10_run2 test00010_run002
mv test10_run10 test00010_run010

If you then pipe it to sh, it will get executed.

Upvotes: 1

Related Questions