Prepend folder names to file name and flatten file structure
I have a bunch of folders with various names, for example
2013-02 Snow and birds
PICT0001.jpg
PICT0002.jpg
2013-06 Bicycle trip
edited_panorama.jpg
From phone
DCIM0001.jpg
DCIM0002.jpg
DCIM0003.jpg
From camera
DSLR
PICT0001.raw
PICT0002.raw
Compact
S0000001.jpg
As you can see, the general structure is a varying level of nested subfolders containing images. What I want to do is to flatten the structure, adding the name of each level of subfolders to the filename, like this:
2013-02 Snow and birds_PICT0001.jpg
2013-02 Snow and birds_PICT0002.jpg
2013-06 Bicycle trip_edited_panorama.jpg
2013-06 Bicycle trip_From phone_DCIM0001.jpg
2013-06 Bicycle trip_From phone_DCIM0002.jpg
2013-06 Bicycle trip_From phone_DCIM0003.jpg
2013-06 Bicycle trip_From camera_DSLR_PICT0001.raw
2013-06 Bicycle trip_From camera_DSLR_PICT0002.raw
2013-06 Bicycle trip_From camera_Compact_S0000001.jpg
How can this be achieved by using either a Terminal script or any other kind of script? I have found some similar solutions, but they all seem to rely on a fixed level of subfolders, whereas my folder structure is varying.
Tricky one, especially if you want to keep all the spaces etc. Run the following in the top directory (the one which contains 2013-02 Snow and birds
etc):
find . -type f -exec sh -c 'for f do x=${f#./}; echo mv "$x" "${x////_}"; done' {} +
The assignment to x
gets rid of the leading ./
from find
, the ${x////_}
replaces all (remaining) occurrences of /
with _
.
Also, I've protected the actual mv
with an echo
so you can verify first whether the commands look ok. Rerun the command without the echo
to actually rename/move the files.
@nohillside 's answer above is great actually. The only issue with it is that it does not account for files with spaces in them. That will cause some of the commands to fail.
So I'm putting an answer here that would support that as well.
This will just echo
out the mv
commands:
find . -type f -exec sh -c 'for f do x=${f#./}; y="${x// /_}"; echo "mv ${x// /\ } ${y////-}"; done' {} +
This will just echo
out the mv
commands into your pasteboard directly:
find . -type f -exec sh -c 'for f do x=${f#./}; y="${x// /_}"; echo "mv ${x// /\ } ${y////-}"; done' {} + | pbcopy
[Careful] This will immediately run the mv
commands for you:
find . -type f -exec sh -c 'for f do x=${f#./}; y="${x// /_}"; eval "mv ${x// /\ } ${y////-}"; done' {} +