Search code examples
shellunixsequentialbatch-rename

Adding sequential numbers to the end of file names - Shell Script


I am trying to add sequential four digit numbers to the end of file names during a rename script. The problem I am running into is that it only pads the first file and the numbers added are not sequential. Here is my script so far:

Starting file names:

FILE-1.png
FILE-5.png
FILE-14.png
FILE-99.png
FILE-167.png
FILE-199.png
FILE-278.png
FILE-455.png

Script:

a=`printf '%04d' "1"`

cd /${1-$PWD}

for i in *.png;
    do mv $i `printf output.%04d.$a.png $(echo $i | sed 's/[^0-9]*//g')`;
    let a=a+1 
done

EDIT: I changed the script a bit incorporating the fmt variable at the top. But I still would like it to name the second set of digits in numerical order of the first set of numbers, as in my Desired output below.

fmt=output.%04d
n=1

cd /${1-$PWD}

for i in *.png;
    do mv $i `printf $fmt.%04d.png $(echo $i | sed 's/[^0-9]*//g') "$n"`;
    n=$((n+1))
done

My new output:

output.0001.0001.png
output.0005.0007.png
output.0014.0002.png
output.0099.0008.png
output.0167.0003.png
output.0199.0004.png
output.0278.0005.png
output.0455.0006.png

Original output:

output.0001.0001.png
output.0005.7.png
output.0014.2.png
output.0099.8.png
output.0167.3.png
output.0199.4.png
output.0278.5.png
output.0455.6.png

Desired output:

output.0001.0001.png
output.0005.0002.png
output.0014.0003.png
output.0099.0004.png
output.0167.0005.png
output.0199.0006.png
output.0278.0007.png
output.0455.0008.png

As always any help is much appreciated!


Solution

  • So .. you're using printf for formatting. That's a good start. But you're not using it consistently. The part that's not formatted.. Why not just format it?

    #!/bin/sh
    
    fmt="output.%04d.%04d.png"
    
    cd /${1-$PWD}
    
    n=1
    for file in *.png; do
        fn="${file%.*}"; fn="${fn#*-}"
        mv "$file" "$(printf "$fmt" "$fn" "$n")"
        n=$((n+1))
    done
    

    Note that the first line within the loop simply strips the number out of $file, first by taking off everything from the dot to the end, then by taking off everything from the start to the dash. You'll have to adjust this if your files are not actually formatted as they are in your question.

    Oh, and Happy Thanksgiving. :-)


    UPDATE per comments

    The expansion above of *.png will obviously order things alphabetically, such that you FILE-5.png follows FILE-455.png, etc.

    A number of tools can help you get a "natural" sort order. In particular, if you're using Linux, your ls and sort probably come from GNU coreutils, which means you can ls -v or sort -V to get a natural sort order. But you haven't specified that you're using Linux, and besides, parsing ls is a bad idea. But bash's internal pattern matching and pathname expansion does not handle natural sorts.

    In this particular case, since you're dealing (at least in your question) with highly predictably formatted filenames, we can likely safely parse ls output and sort it using command line tools.

    If you're using Linux and bash, the for line above can be replaced with:

    shopt -s extglob
    ls -v FILE-+([0-9]).png | while read file; do
    

    This sets bash's extglob shell option, then uses ls -v (which is the Linux dependency) to show a restricted view of the files. When parsing ls, you don't want to make the mistake of constructs like *.png, as you don't want to have to spend time predicting what will happen if there's a newline inside a filename.

    If you're using FreeBSD or OSX, or are not using bash, extra measures are required, as there is ls -v and no extglob in Almquish shell:

    ls -f FILE-*.png | egrep '^FILE-[0-9]+\.png$' | sort -t- -k2n | while read file; do
      if [ ! -f "$file" ]; then
        continue
      fi
    

    This breaks down as follows:

    • ls -f does no sorting on the directory. The filespec restricts the view somewhat.
    • grep is used to enforce filename format, as this pattern can't be represented completely in shell expansion.
    • sort -t- -k2n delimits fields with a hyphen, then sorts numerically on the second field.
    • The if inside the loop makes sure that we someone isn't screwing with us by making a filename like FILE-1.png\nFILE-2.png.