I need to change my directory structure for my photos from /YEAR/MONTH/DAY/ to /YEAR-MONTH-DAY

I know this can sort of be done with exiftool, but it will only work on files containing EXIF-tags, and I have xmp-sidecar files corresponding to each image, and these need come along too. So I recon script of some kind would be the best way.

I sat down and tried to learn RegEx, sed, bash and what not, and given enough time, I guess I should be able to figure this one out, but right now I am in a hurry, so any help would be appreciated.


  • an example would be better. Do you want to rename files? Jun 14 '14 at 14:37
  • Do you mean you already have a structure and you want to flatten it? Do you want to create one directory per day? Should the path change, but the filenames stay?
    – choroba
    Jun 14 '14 at 15:08

The rename utility in Ubuntu can rename directory structures but it won't clean up after itself.

rename 's#(.+)/(.+)/(.+)#$1-$2-$3#' */*/*/

Stick -vn on the end if you just want it to tell you what it's going to do before it renames anything, but here's a little test harness that shows you what's possible:

$ mkdir -p 2014/06/15
$ touch 2014/06/15/photo_{001..003}.jpg
$ tree
└── 2014
    └── 06
        └── 15
            ├── photo_001.jpg
            ├── photo_002.jpg
            └── photo_003.jpg

$ rename 's#(.+)/(.+)/(.+)#$1-$2-$3#' */*/*/
$ tree
├── 2014
│   └── 06
└── 2014-06-15
    ├── photo_001.jpg
    ├── photo_002.jpg
    └── photo_003.jpg

Simply put it's being fed the third-level directories and renaming is reading the earlier two segments and renaming it, sticking it in the current directory. As you can see there will be a load of year directories. Assuming they're empty you could clean up with something like (and I pray you check they're empty first):

find -maxdepth 1 -type d -regex '\./[0-9][0-9][0-9][0-9]' -exec rm -irf "{}" \;

I'm using the -i option to force it to ask you before deleting every file. Remove that at your own risk.

cd base
# the trailing slash in the pattern limits the results to directories, not files
for subdir in */*/*/; do
    # $subdir is now "year/mon/day/"
    subdir=${subdir%/}        # remove the trailing slash
    newdir=${subdir//\//-}    # replace all slashes with hyphens
    mkdir "$newdir"
    mv "$subdir"/* "$newdir"

Try this:

for y in *; do
  if cd $y; then
    for m in *; do
      if cd $m; then
           for d in *; do
          if cd $d; then
            mv $d $TOP/$y-$m-$d -R 
        cd .. && rmdir $m
    cd .. && rmdir $y
$ fn="/media/drive/folder/2014/06/14/name.jpg" ; \
echo "mv $fn $(echo $fn | sed -re 's,([0-9]+)/,\1-,g')"
mv /media/drive/folder/2014/06/14/name.jpg /media/drive/folder/2014-06-14-name.jpg
$ find -type f | tee LISTOFFILES.txt
$ cat LISTOFFILES.txt | while read p  ; do # loop through all files, one at a time
f="${p##*/}" ; bn=${f%%\.*} ; op="${p%/*}/" ; 
echo -e "$f\n$bn\n$op"
# $p - complete path to original file
# $f - filename with extension
# $bn - basename, filename without extension
# $op - orignal PATH, filename and extensions stripped off

# add your conversion here - based on the above
# mkdir -p "$p" # probably required
# mv "$p" "..." # 

Now, if you read the above THOROUGHLY you will find all bits and pieces you need to accomplish this task.

As I cannot tell how you HAVE IT nor HOW YOU WANT IT - I cannot finish it for you.

has TWO Bash guides to read and look through - have a go.

I'm leaving it here. This is not a service, I did it due to curiosity only.
And remember - there MAY be faults in there, as I have not tested it fully.
YOU may introduce faults you didn't think of... :-)

Good luck! ;-) you need it.

  • When you don't have enough details to confidently answer a question, there's a fair chance that the question should be closed as Unclear. Apr 22 '20 at 9:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.