0
votes

I am trying to mass rename folders and their respective files by use of the command "mv" but its not going very well, I would like the folders to be renamed into:

1/
2/
3/

etc.

The files in each folder should in this process also be renamed while keeping their file extension.

1.png
2.gif
3.jpg

etc

Very thankful for your help

2

2 Answers

2
votes

EDIT: Better approach with a function rather than recursive calls to the same script.

Here's hoping I got all corner cases. This descends into directories recursively to handle deeply nested directory trees.

Caveat: Because the script takes care to not overwrite existing files, gaps may appear in the numbering in some corner cases -- if there is a file 0.txt in a directory and the first file that is handled in that directory is a .txt file, it will be moved to 1.txt. This also happens if the first file that is handled is 0.txt, so running the script twice will change the numbering, and running it again will change it back.

So here's the code:

#!/bin/bash

handle_directory() {
    local counter=0

    for i in *; do
        # if the file is a directory (but not a symlink),
        #  handle it before moving
        if [ -d  "$i" ] && ! [ -h "$i" ]; then
            cd "$i"
            handle_directory
            cd ..
        fi

        # extract suffix
        suffix="${i##*.}"

        if [ "$suffix" != "$i" ]; then
            extension=".$suffix"
        else
            # If there is no filename extension, the counter
            # is the whole filename. Without this, we'd get
            # 0.Makefile and suchlike.
            extension=""
        fi

        # find a filename that isn't already taken
        # this may lead to gaps in the numbering.
        while dest="$counter$extension" && [ -e "$dest" ]; do
            let ++counter
        done

        echo mv "$i" "$dest"
        let ++counter
    done
}

# if a parameter was given, go there to handle it.
# otherwise handle the local directory. 
if ! [ -z "$1" ] && ! cd "$1"; then
    echo "Could not chdir to directory $1"
    exit -1
fi

handle_directory

The general idea is a depth-first search of the directory tree in question. Like any tree, the directory tree is best handled recursively, and the function essentially boils down to: Walk through all things in this directory, if they are a directory, descend and handle it, then find an appropriate file name and rename the thing whether it is a directory or not.

Things used:

local counter=0 # declares a function-local variable counter and initializes it
                # to 0. crucially, that it is local means that every invocation
                # of handle_directory has its own counter.
[ -d "$i" ]     # tests if "$i" is a directory
[ -h "$i" ]     # tests if "$i" is a symlink
! [ ... ]       # negates the test. ! [ -h "$i" ] is true if "$i" is NOT a symlink
"${i##*.}"      # a bashism that cuts off the longest prefix that matches the pattern
[ -e "$dest" ]  # tests if "$dest" exists
$1              # the first parameter with which the script is called
[ -z "$1" ]     # tests if "$1" is an empty string
! cd "$1"       # descends into the directory "$1". true if that failed.

Manpages to read to further understanding:

man test
man bash # that's the big one.
1
votes

Is it a single level directory? Or do you have subdirectories?

Something like this?

Before:

.
├── folderE
│   ├── conf.properties
│   ├── omg.avi
│   └── test-diff.diff
├── folder_a
│   ├── first.png
│   ├── main.jpg
│   └── second.gif
├── folder_d
│   ├── another.zip
│   └── one.mpeg
└── with space
    └── file with spaces too.mov

Command:

countDir=1
for dir in *; do
  cd "$dir"; 
  countFile=1

  for file in *; do 
    mv "$file" $countFile.${file#*.}
    ((countFile++))
  done

  cd ..
  mv "$dir" $countDir
  ((countDir++))
done

Or, the same in one line:

countDir=1; for dir in *; do cd "$dir"; countFile=1; for file in *; do mv "$file" $countFile.${file#*.}; ((countFile++)); done; cd ..; mv "$dir" $countDir; ((countDir++)); done

After:

.
├── 1
│   ├── 1.properties
│   ├── 2.avi
│   └── 3.diff
├── 2
│   ├── 1.png
│   ├── 2.jpg
│   └── 3.gif
├── 3
│   ├── 1.zip
│   └── 2.mpeg
└── 4
    └── 1.mov

Important: keep in mind that this is just a quick and dirty solution and there's no checking for files/directoris already named "1" "2" etc.