I have decades worth of family photos and videos that are organized and labeled within a directory tree containing thousands of directories. The file paths, in general, all look like:

/my_giant_pictures_dir/year/month_day_"event description"/filename

an example could be:

/my_giant_pictures_dir/2016/07_27_Roadtrip to Antarctica/IMG_00001.jpg

note that the "event," or deepest directories may contain .jpg, .mp4, and .mts files.

My problem with this convention is that I end up with many deepest media-containing directories that, because they are organized first by date, contain only one or a few files. (Days where only one picture was taken, for example). This makes looking through, say, all the pictures taken during a three month period, rather tedious.

I would like to restructure all of this media by keeping everything in chronological order as it is, but by eliminating all of the intermediate subdirectories after the year. I would like to do this by prepending every media filename with its parent directory's "month_day_event" name, for example:

instead of:

/Pictures/2016/07_27_Roadtrip to Antarctica/IMG_00001.jpg

...I would rather have:

/Pictures/2016/07_27_Roadtrip to Antarctica_IMG_00001.jpg

Is there a bash command that would do this for every .jpg, .mp4, and .mts file in my entire pictures directory, then after the filename prepend, move them out of their parent directory and into the next highest directory, which in every case is the year directory?

And maybe it is asking too much, but could it also delete the then empty subdirectories that are contained within the year directories? I don't mind running multiple commands if necessary because, either way, your answer will be saving me countless hours.

Thank you very much in advance!

1 Answer 1



find /my_giant_pictures_dir/ -type f -regex '.*\(jpg\|mp4\|mts\)' -execdir bash -c 'mv "$1" "../${PWD##*/}_${1#./}"' Bash {} \;

How it works

  • find /my_giant_pictures_dir/ -type f -regex '.*\(jpg\|mp4\|mts\)'

    This looks for all regular files anywhere underneach /my_giant_pictures_dir/whose names end with jpg, mp4, or mts. Each such file, in turn, is passed on to the -execdir.

  • -execdir bash -c 'mv "$1" "../${PWD##*/}_${1#./}"' Bash {} \;

    This moves the file to its parent directory while also renaming it.

    In more detail, -execdir bash -c '...' Bash {} \; changes the current working directory to the directory that the file in is and executes bash -c '...' with arguments Bash and the file name {}.

    bash -c '...' runs the command in the single-quotes with $0 having the value of Bash and $1 having as its value the name of the file.

    Inside the single-quotes, we have mv "$1" "../${PWD##*/}_${1#./}" This moves the file, $, to its parent directory, ../ with a name of ${PWD##*/}_${1#./}.

  • 1
    Amazing, it worked perfectly. I actually had way more than three file types so I had to add to the search criteria following your syntax, but yes, it all worked great and took hardly any time at all. Thank you so much!
    – Ross Inman
    Aug 2, 2016 at 5:09

You must log in to answer this question.

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