rename all jpeg files sequentially in all nested subfolders and move to a new folder

My question is similar to this one

https://stackoverflow.com/questions/54811336/rename-jpeg-files-sequentially-in-multiple-subfolders

but my folder structure is like this

├── mainfolder
|    ├── subfolder1
|    |   ├── 01folder
             |--02folder
                 |--03folder 
                      |--- 0001.jpg
|    |                └── 0002.jpg
                      ...
|    |                └── 0250.jpg
|    |
|    ├── subfolder2
           |── 01folder
                |--02folder
                     |--03folder 
|    |                  ├── 0001.jpg
|    |                  └── 0002.jpg
                         ...
|    |                  └── 0250.jpg
|    |
|    ├── subfolder3
           ├── 01folder
                 |--02folder
                     |--03folder  
|    |                   ├── 0001.jpg
|    |                   └── 0002.jpg
                         ...
|    |                   └── 0250.jpg
…

all folders have jpg images numbered from 1 to 250 for a total of 100000 images. I would like to move all the files and rename them before moving them so the folder structure looks like this

├── newfolder
|    |--1.jpg
|    |--2.jpg
|    |--3.jpg
...
|    |--100000.jpg

I am currently using this

to move all the files from the nested subdirectories to a new directory

find ~/mainfolder/ -type f -print0 | xargs -0 mv -t ~/newfolder/

How do I move files out of nested subdirectories into another folder in ubuntu? (Trying to strip off many subfolders)

not sure how to rename them as well though as they will overwrite each other due to having the same name when moving resulting in only 250 files instead of 100000 files.


Solution 1:

With Larry Wall's rename/prename(*) you can easily "flatten" the structure:

cd mainfolder
rename -n 's:/:-:g' */*/*.jpg

This will move subfolder1/0001.jpg to subfolder1-0001.jpg (in mainfolder) because rename edits the whole input path and then does the equivalent of mv using source and edited names.

  • g in the edit means to replace all occurrences, useful if you have more than one level of subdirectories.
  • -n just shows what it would do without doing anything
  • remove or replace by -v for the actual action.
  • by default no overwrite can happen (you need a -f) but normally since this includes subdirectories in the names you shouldn't have name clashes anyway.
  • once everything is in mainfolder, it is easy to do a bulk move elsewhere if needed.
  • if the files are in various levels of nesting, use shopt -s globstar and use ** to denote all levels:

    rename s:/:-:g **/*.jpg
    

For instance:

Starting with

.
├── sub1
│   ├── subsub1
│   │   ├── 001.jpg
│   │   ├── 002.jpg
│   │   └── 003.jpg
│   └── subsub2
│       ├── 001.jpg
│       ├── 002.jpg
│       └── 003.jpg
└── sub2
    ├── subsub1
    │   ├── 001.jpg
    │   ├── 002.jpg
    │   └── 003.jpg
    └── subsub2
        ├── 001.jpg
        ├── 002.jpg
        └── 003.jpg

Execute:

rename s:/:-:g */*/*.jpg

This results in:

.
├── sub1
│   ├── subsub1
│   └── subsub2
├── sub1-subsub1-001.jpg
├── sub1-subsub1-002.jpg
├── sub1-subsub1-003.jpg
├── sub1-subsub2-001.jpg
├── sub1-subsub2-002.jpg
├── sub1-subsub2-003.jpg
├── sub2
│   ├── subsub1
│   └── subsub2
├── sub2-subsub1-001.jpg
├── sub2-subsub1-002.jpg
├── sub2-subsub1-003.jpg
├── sub2-subsub2-001.jpg
├── sub2-subsub2-002.jpg
└── sub2-subsub2-003.jpg

And you can get rid of the subdirectories using

rm -r sub*/

(final slash is important! it restricts the match to directories)

So finally:

.
├── sub1-subsub1-001.jpg
├── sub1-subsub1-002.jpg
├── sub1-subsub1-003.jpg
├── sub1-subsub2-001.jpg
├── sub1-subsub2-002.jpg
├── sub1-subsub2-003.jpg
├── sub2-subsub1-001.jpg
├── sub2-subsub1-002.jpg
├── sub2-subsub1-003.jpg
├── sub2-subsub2-001.jpg
├── sub2-subsub2-002.jpg
└── sub2-subsub2-003.jpg

If you have a huge collection of files, you cannot process in one call because the file expansion of **/*.jpg will generate a list which is too long (/bin/sh: 1: rename: Argument list too long`). So you have to split the work, for instance by calling rename once by directory:

shopt -s globstar 
for d in **/; do rename s:/:-:g "$d"*.jpg; done

(the / in '**/' is important, it restricts the expansion to directories)

If you still get the Argument list too long message then you have a directory with very many files :) You just have to find a way to split the work, for instance:

for f in path/to/hugedir/A*;jpg; do rename s:/:-:g "$d"*.jpg; done
for f in path/to/hugedir/B*;jpg; do rename s:/:-:g "$d"*.jpg; done
[... etc ...]

If this is still too big and or you want to be blunt, you can also call rename once for each file:

shopt -s globstar 
for f in **/*.jpg; do rename s:/:-:g "$f"; done

(this will work because here the expansion of "**/*.jpg" is just a variable in your shell, and not the list of arguments to a command).

PS: bash assumed here, if you are putting this in a script, make sure the "shebang" is /bin/bash, on some systems sh is a lightweight shell (dash) that doesn't support globstar.

(*) name varies depending on distro, rename for Debian/Ubuntu and derivatives, and IIRC prename on RedHat and derivatives.