Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have the following directory structure:

.
├── event-a
│   ├── album-a
│   │   ├── a.jpg
│   │   └── x.png
│   └── album-b
│       ├── a.jpg
│       ├── x.png
│       └── y.gif
└── event-b
    ├── album-x
    │   ├── a.jpg
    │   └── x.png
    └── album-y
        ├── a.jpg
        ├── x.png
        └── y.gif

For each second-level subfolder (named album-foo in the example), I want to sort the files by name and rename them to sequential padded numbers, regardless of their extension. The folders may contain JPG, PNG or GIF images, and any file extensions should be preserved.

So, in this case, I’d like to get this result:

.
├── event-a
│   ├── album-a
│   │   ├── 01.jpg
│   │   └── 02.png
│   └── album-b
│       ├── 01.jpg
│       ├── 02.png
│       └── 03.gif
└── event-b
    ├── album-x
    │   ├── 01.jpg
    │   └── 02.png
    └── album-y
        ├── 01.jpg
        ├── 02.png
        └── 03.gif

The rename utility may be used if that makes anything easier.

The main problem is getting figuring out the number each file should get. Do I need to use a nested for loop to iterate over the event-x and album-x subfolders, then for each inner loop, keep track of the number myself, or is there some clever solution that I’m missing?

share|improve this question
add comment

3 Answers

up vote 7 down vote accepted
for dir in */*; do           # loop over the directories
    (                        # run in a subshell ...
        cd "$dir"            # ... so we don't have to cd back
        files=(*)            # store the filenames in a zero-indexed array

        for index in "${!files[@]}"; do
            file=${files[$index]}
            ext=${file##*.}"
            newname=$(printf "%02d.%s" $((index+1)) "$ext")
            mv "$file" "$newname"
        done
    )
done

Suppose you have a file with no extension. In that case it will have the same name except with leading numbers (e.g. my_file => 05.my_file)

All non-hidden directory entries will be renamed, including directories.

share|improve this answer
    
Thanks! FTR, a friend of mine wrote the following snippet which is very similar to your answer: gist.github.com/2471545 –  Mathias Bynens Apr 23 '12 at 20:01
add comment

You have two sets of problems here:

  1. Recursing into directories.
  2. Restarting the numbering system within each separate directory.

This will handle both conditions smoothly, although you will want to modify the IMAGE_TYPES variable if you decide to include other extensions as well.

#!/bin/bash

shopt -s extglob
shopt -s nocaseglob

IMAGE_TYPES='jpg|png|gif'
IFS=$'\n' dirlist=(`find "$PWD" -type d`)

for dir in "${dirlist[@]}"; do
    cd "$dir"
    ls *.+($IMAGE_TYPES) > /dev/null 2>&1 || continue

    counter=0
    for file in *.+($IMAGE_TYPES); do
        printf -v newname "%.3d.%s" $((counter += 1)) "${file##*.}"
        mv --verbose "$file" "$newname"
    done
done
share|improve this answer
add comment

If you have the name of the file, you can get it's number by running:

ls -1 <your_dir> | grep <file_name> -B 1000 | wc -l

Please tell me if it helps you or not. :)

share|improve this answer
    
Parsing the output of ls is not a good idea. It'll mostly work here, but a wildcard-based solution will completely work, so why settle for second-best? –  Gilles Apr 23 '12 at 23:16
    
"ls -1" give you only the file names if that's what you mean. –  alexarsh Apr 24 '12 at 16:46
    
In most cases, yes. In general, no. Try (in bash/ksh/zsh) touch junk precious $'junk\nprecious'; ls -1 | grep junk | xargs -l rm. You didn't really care about that precious file, did you? Not to mention that restriction to at most 1000 files, or the fact that you find 03.jpg when you're looking for 3.jpg. I retract my comment for second-best, this is really broken, and it's incomplete to boot. –  Gilles Apr 24 '12 at 17:26
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.