Search code examples
bashbatch-rename

rename multiple files from different directories at once


I have thousands of files in different folders that I would like to rename with their directories and, but less importantly, edit a few details after.

As a structure example:

FOLDER A

---> folder 1

- 1.jpg
- 2.jpg

---> folder 2

- 1.jpg
- 2.jpg

FOLDER B

---> folder 1

- 1.jpg
- 2.jpg

...

The end name should be: FOLDER A_folder 1_1.jpg ...etc

and the less important edit: FOLDER A - 1_1.jpg ...etc

I don't know if it's relevant, but I have Ubuntu and from the terminal I've test tried this:

for file in Folders*/*/*/*.jpg; do
  mv "$file" "${file//\//_}";
done

It correctly renames the files with their paths but simultaneously extracts them and put them in the main folder, how do I avoid that?


Solution

  • You need to add the path to the "target" file (the second parameter of the mv command) since you replaced all / with _ practically removing the path and making it part of the new name:

    for file in FOLDER*/*/*; do
        mv "$file" "$(dirname "$file")/${file//\//_}";
    done
    

    TEST

    Directories and files before running the loop:

    $ find .
    .
    ./FOLDER B
    ./FOLDER B/folder 2
    ./FOLDER B/folder 2/2.jpg
    ./FOLDER B/folder 2/1.jpg
    ./FOLDER B/folder 1
    ./FOLDER B/folder 1/2.jpg
    ./FOLDER B/folder 1/1.jpg
    ./FOLDER A
    ./FOLDER A/folder 2
    ./FOLDER A/folder 2/2.jpg
    ./FOLDER A/folder 2/1.jpg
    ./FOLDER A/folder 1
    ./FOLDER A/folder 1/2.jpg
    ./FOLDER A/folder 1/1.jpg
    $
    

    Renaming the files and the results:

    $ for file in FOLDER*/*/*; do
    >     mv "$file" "$(dirname "$file")/${file//\//_}";
    > done
    $ find .
    .
    ./FOLDER B
    ./FOLDER B/folder 2
    ./FOLDER B/folder 2/FOLDER B_folder 2_1.jpg
    ./FOLDER B/folder 2/FOLDER B_folder 2_2.jpg
    ./FOLDER B/folder 1
    ./FOLDER B/folder 1/FOLDER B_folder 1_1.jpg
    ./FOLDER B/folder 1/FOLDER B_folder 1_2.jpg
    ./FOLDER A
    ./FOLDER A/folder 2
    ./FOLDER A/folder 2/FOLDER A_folder 2_1.jpg
    ./FOLDER A/folder 2/FOLDER A_folder 2_2.jpg
    ./FOLDER A/folder 1
    ./FOLDER A/folder 1/FOLDER A_folder 1_1.jpg
    ./FOLDER A/folder 1/FOLDER A_folder 1_2.jpg
    $
    

    As for your "less important edit," you can rename the files again, like this:

    for file in FOLDER*/*/*; do
        mv "$file" "${file//_folder/ -}";
    done
    

    Note that you don't need to add the path here since the _folder to - substitution done to the file variable only affects the file name and not the path.

    Final result:

    $ find .
    .
    ./FOLDER B
    ./FOLDER B/folder 2
    ./FOLDER B/folder 2/FOLDER B - 2_1.jpg
    ./FOLDER B/folder 2/FOLDER B - 2_2.jpg
    ./FOLDER B/folder 1
    ./FOLDER B/folder 1/FOLDER B - 1_1.jpg
    ./FOLDER B/folder 1/FOLDER B - 1_2.jpg
    ./FOLDER A
    ./FOLDER A/folder 2
    ./FOLDER A/folder 2/FOLDER A - 2_1.jpg
    ./FOLDER A/folder 2/FOLDER A - 2_2.jpg
    ./FOLDER A/folder 1
    ./FOLDER A/folder 1/FOLDER A - 1_1.jpg
    ./FOLDER A/folder 1/FOLDER A - 1_2.jpg