You can also use the -mindepth
option:
find . -type f -mindepth 2 -exec mv -i -- {} . \;
(Together with -maxdepth
you could also limit the hierarchy levels from which to collect the files.)
I used mv -i
(“interactive”) to make mv
ask before overwriting files. With a lot of subdirectories, there may be name clashes you'd like to be warned about.
The --
option stops option processing, so mv
doesn't get confused by filenames starting with a hyphen.
Clean up the whole bunch of empty subdirectories with
find . -depth -mindepth 1 -type d -empty -exec rmdir {} \;
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.
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.
Best Answer
With Perl's standalone
rename
command:Output:
If everything looks fine, remove option
-n
.