You can do this with rename
using bash
’s globstar
option:
shopt -s globstar
rename -n 's/.*\//$&JPEG\//' **/*.jpeg
rename -n 's/.*\//$&TIF\//' **/*.tif
The -n
option lets it just output what it would do, remove it to actually perform the moving. What happens here is that rename
goes over each e.g. .jpeg
file in any subdirectory thanks to **
matching any number of subdirectories. It substitutes its path (everything until the last /
) with itself ($&
) followed by JPEG/
, effectively moving the file to this directory.
If your list of arguments is too long the commands above will throw an error. Use this approach to work around the shell’s ARG_MAX
limit:
printf '%s\0' **/*.jpeg | xargs -0 rename -n 's/.*\//$&JPEG\//'
printf '%s\0' **/*.tif | xargs -0 rename -n 's/.*\//$&TIF\//'
This uses the shell builtin printf
to build a zero-delimited argument list which is piped to xargs
which calls rename
with the maximum number of arguments.
Example run
$ tree
.
├── subdirectory-A
│ ├── 1.jpeg
│ ├── 1.tif
│ ├── 2.jpeg
│ ├── 2.tif
│ ├── JPEG
│ └── TIF
└── subdirectory-B
├── 1.jpeg
├── 1.tif
├── 2.jpeg
├── 2.tif
├── JPEG
└── TIF
$ shopt -s globstar
$ rename -n 's/.*\//$&JPEG\//' **/*.jpeg
rename(subdirectory-A/1.jpeg, subdirectory-A/JPEG/1.jpeg)
rename(subdirectory-A/2.jpeg, subdirectory-A/JPEG/2.jpeg)
rename(subdirectory-B/1.jpeg, subdirectory-B/JPEG/1.jpeg)
rename(subdirectory-B/2.jpeg, subdirectory-B/JPEG/2.jpeg)
$ rename 's/.*\//$&JPEG\//' **/*.jpeg
$ printf '%s\0' **/*.tif | xargs -0 rename 's/.*\//$&TIF\//'
$ tree
.
├── subdirectory-A
│ ├── JPEG
│ │ ├── 1.jpeg
│ │ └── 2.jpeg
│ └── TIF
│ ├── 1.tif
│ └── 2.tif
└── subdirectory-B
├── JPEG
│ ├── 1.jpeg
│ └── 2.jpeg
└── TIF
├── 1.tif
└── 2.tif