6

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.

3 Answers 3

8

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.

3
  • Worked great! Although, it did miss 3 files out of 8300 for some inexplicable reason. Luckily I expanded all folders and subfolders after moving the files, just to make sure! The 3 files were all nested only 1 level deep and in folders where there had previously been other files with similar names (PICT0001.JPG etc).
    – Magnus
    Apr 7, 2018 at 4:08
  • @nohillside Thanks for providing this command! Is there any way to change it, so that the files are not moved out of their original folders?
    – Philipp
    Feb 12, 2022 at 9:45
  • 1
    @Philipp The whole idea here is to move the files out of their original folders. If you have another problem you want to solve, please ask a new question.
    – nohillside
    Feb 12, 2022 at 9:58
3

@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' {} +
4
  • Now I'm curious :-) Which specific scenario with spaces makes my code snippet fail?
    – nohillside
    Jul 11, 2019 at 15:54
  • Just if the filename includes spaces. It does not escape the space characters in the echo so with a direct copy and paste, it works for most files except for those ones spitting out the error explaining the usage of mv usage: mv [-f | -i | -n] [-v] source target
    – xemacobra
    Jul 11, 2019 at 16:06
  • 4
    Well, the idea is not to copy/paste the result but to remove the echo once the output looks ok. But as usual there are lots of ways :-)
    – nohillside
    Jul 11, 2019 at 16:56
  • There is an issue with eval on MacOS when image name contains parenthesis: Image(30).jpg: eval: line 0: syntax error near unexpected token `(' May 29, 2021 at 12:11
1

For purposes of this script, I had all of your folders contained in a folder named "Filez" on my desktop. This script starts with a "choose folder" dialog to choose the containing folder in which the folders "2013-06 Bicycle trip" & "2013-02 Snow and birds" reside. This will return all of the individual files within all folders and subfolders, of which you want the names to be changed.

set mainFolder to choose folder
set posixPathOfMainFolder to POSIX path of mainFolder
tell application "Finder"
    set theNames to files of entire contents of mainFolder as alias list
end tell
set newFileNames to {}
repeat with aFile in theNames
    set text item delimiters to {posixPathOfMainFolder, "/"}
    set aFile to POSIX path of aFile
    set tempName to text items -1 thru 2 of aFile
    set text item delimiters to "_"
    set end of newFileNames to tempName as text
    set text item delimiters to ""
    tell application "Finder"
        set the name of (aFile as POSIX file as alias) to last item of newFileNames as text
    end tell
end repeat
tell application "Finder"
    set theNames to files of entire contents of mainFolder as alias list
    move theNames to mainFolder
    delete folders of mainFolder
end tell

enter image description here

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .