Terminal loop command to change created date for all files in a large directory

finderterminal

I have a directory with roughly 3000 video/subtitle files nested in slightly varying levels of sub directories that I would like to change the 'date created' on.

Almost every file has a year in the name and the year is always formatted as '[YYYY]' and I would like to use touch to change the date created for each file to the aforementioned year. Date, month, hour, and minute don't particularly matter so they could just be YYYY01010000 for all I care.

Where it gets tricky is that I would also like to do the same for the directory containing each file(s). (it is a directory of the exact same name as the file). There is at least one file with no [YYYY] and possibly more so there would have to be a "leave it alone" case but I know that more than 99% have the year included.

I am just not experienced enough with terminal to risk throwing everything out of organization with a mistake.

the majority of files follow this directory structure (file types vary):

'Videos'/'Name [YYYY]'/'Name [YYYY]'.mp4

OR for related groups of videos

'Videos'/'Name [ALL]'/'Name1 [YYY1]'/'Name1 [YYY1]'.mp4
'Videos'/'Name [ALL]'/'Name2 [YYY2]'/'Name2 [YYY2]'.avi
'Videos'/'Name [ALL]'/'Name3 [YYY3]'/'Name3 [YYY3]'.mkv

As for the 'Name [ALL]' directories I think I can handle those as odds and ends as they should be easy to find if they have normal created dates as opposed to the standardized output of the touch loop.

EDIT —-

/Volumes/MediaDrive/Media/Video\ Media
├── Movies
│   ├── 12\ Angry\ Men\ [1957]
│   │   └── 12\ Angry\ Men\ [1957].avi
│   ├── 12\ Years\ a\ Slave\ [2013]
│   │   └── 12\ Years\ a\ Slave\ [2013].avi
│   ├── 13\ Sins\ [2014]
│   │   └── 13\ Sins\ [2014].mkv
│   ├── 2\ Guns\ [2013]
│   │   └── 2\ Guns\ [2013].mp4
│   ├── 2001\ A\ Space\ Odyssey\ [1968]
│   │   ├── 2001\ A\ Space\ Odyssey\ [1968].mp4
│   │   └── 2001\ A\ Space\ Odyssey\ [1968].srt
│   ├── 2012\ [2009]
│   │   └── 2012\ [2009].avi
│   ├── 21\ Jump\ Street\ [ALL]
│   │   ├── 21\ Jump\ Street\ [2012]
│   │   │   └── 21\ Jump\ Street\ [2012].mp4
│   │   └── 22\ Jump\ Street\ [2014]
│   │       └── 22\ Jump\ Street\ [2014].mp4
│   ├── 28\ Days\ Later\ [ALL]
│   │   ├── 28\ Days\ Later\ [2002]
│   │   │   └── 28\ Days\ Later\ [2002].mp4
│   │   └── 28\ Weeks\ Later\ [2007]
│   │       └── 28\ Weeks\ Later\ [2007].divx

This is to apply to only the movies directory so the others at the top of the tree will not be involved.

To my best knowledge all files will be either:
1 – alone in a directory sharing their name.
2 – with 1-5 subtitles files sharing their name + language on the end, inside a directory sharing their name.
3 – in a directory that could or could not be sharing their name followed by '[ALL]' which will contain either Case 1 or Case 2.
4 – Any version of the above 3 cases, but without a year appended (less than 1% so can be skipped).

Best Answer

I took a stab at writing a script that I think should work for you. You mentioned that you are not too experienced with terminal, so to use it, do the following:

  1. cd into the root of your Movies folder
  2. Create an open a new file (can be named anything, I used change-date) using touch change-date; open -e change-date. This will open a new file in TextEdit.
  3. Copy paste the script below into the open file.
  4. Make the script executable by running chmod +x change-date
  5. Run the script using ./change-date
  6. (Optional) Delete the script using rm change-date

You can replace all instances of touch with echo touch if you would like to test what the script will do. This will cause it to print all of the touch commands it will run, so you can look through the output and make sure it looks correct before removing the echo commands.

#!/usr/bin/env zsh

# recursively loop through all directories in the current directory (should be Movies)
for dir in **/*(/); do
    # use a regex with sed to set $year to whatever is between [ and ] if it is a series of 4 digits
    # if there aren't brackets, or the contents inside the brackets aren't 4 digits, year will be an empty string
    year=$(echo $dir | sed -En "s/.*\[([0-9]{4})\]/\1/p")

    # if the length of year is 4 characters long
    if [[ ${#year} -eq 4 ]] then
        # set the modification time of $dir to Jan 1 of $year
        touch -t "$year"01010000.00 $dir

        # loop through all files in $dir
        for file in $dir/*(.); do
            # set the modification time of $file to Jan 1 of $year
            touch -t "$year"01010000.00 $file
        done
    fi
done

I'm not the most experienced at writing my own shell scripts, but this seemed to work for me. If someone with more experience would like to correct an inefficiency or some other mistake, please feel free.

Before running this script, my directory looked like this:

$ tree -D
Movies
├── [Jan 10 00:22]  12\ Angry\ Men\ [1957]
│   └── [Jan 10 00:22]  12\ Angry\ Men\ [1957].avi
├── [Jan 10 00:22]  12\ Years\ a\ Slave\ [2013]
│   └── [Jan 10 00:22]  12\ Years\ a\ Slave\ [2013].avi
├── [Jan 10 00:22]  13\ Sins\ [2014]
│   └── [Jan 10 00:22]  13\ Sins\ [2014].mkv
├── [Jan 10 00:22]  2\ Guns\ [2013]
│   └── [Jan 10 00:22]  2\ Guns\ [2013].mp4
├── [Jan 10 00:22]  2001\ A\ Space\ Odyssey\ [1968]
│   ├── [Jan 10 00:22]  2001\ A\ Space\ Odyssey\ [1968].mp4
│   └── [Jan 10 00:22]  2001\ A\ Space\ Odyssey\ [1968].srt
├── [Jan 10 00:22]  2012\ [2009]
│   └── [Jan 10 00:22]  2012\ [2009].avi
├── [Jan 10 00:22]  21\ Jump\ Street\ [ALL]
│   ├── [Jan 10 00:22]  21\ Jump\ Street\ [2012]
│   │   └── [Jan 10 00:22]  21\ Jump\ Street\ [2012].mp4
│   └── [Jan 10 00:22]  22\ Jump\ Street\ [2014]
│       └── [Jan 10 00:22]  22\ Jump\ Street\ [2014].mp4
├── [Jan 10 00:22]  28\ Days\ Later\ [ALL]
│   ├── [Jan 10 00:22]  28\ Days\ Later\ [2002]
│   │   └── [Jan 10 00:22]  28\ Days\ Later\ [2002].mp4
│   └── [Jan 10 00:22]  28\ Weeks\ Later\ [2007]
│       └── [Jan 10 00:22]  28\ Weeks\ Later\ [2007].divx

And after running, it looked like this:

$ tree -D
Movies
├── [Jan  1  1957]  12\ Angry\ Men\ [1957]
│   └── [Jan  1  1957]  12\ Angry\ Men\ [1957].avi
├── [Jan  1  2013]  12\ Years\ a\ Slave\ [2013]
│   └── [Jan  1  2013]  12\ Years\ a\ Slave\ [2013].avi
├── [Jan  1  2014]  13\ Sins\ [2014]
│   └── [Jan  1  2014]  13\ Sins\ [2014].mkv
├── [Jan  1  2013]  2\ Guns\ [2013]
│   └── [Jan  1  2013]  2\ Guns\ [2013].mp4
├── [Jan  1  1968]  2001\ A\ Space\ Odyssey\ [1968]
│   ├── [Jan  1  1968]  2001\ A\ Space\ Odyssey\ [1968].mp4
│   └── [Jan  1  1968]  2001\ A\ Space\ Odyssey\ [1968].srt
├── [Jan  1  2009]  2012\ [2009]
│   └── [Jan  1  2009]  2012\ [2009].avi
├── [Jan 10 00:22]  21\ Jump\ Street\ [ALL]
│   ├── [Jan  1  2012]  21\ Jump\ Street\ [2012]
│   │   └── [Jan  1  2012]  21\ Jump\ Street\ [2012].mp4
│   └── [Jan  1  2014]  22\ Jump\ Street\ [2014]
│       └── [Jan  1  2014]  22\ Jump\ Street\ [2014].mp4
├── [Jan 10 00:22]  28\ Days\ Later\ [ALL]
│   ├── [Jan  1  2002]  28\ Days\ Later\ [2002]
│   │   └── [Jan  1  2002]  28\ Days\ Later\ [2002].mp4
│   └── [Jan  1  2007]  28\ Weeks\ Later\ [2007]
│       └── [Jan  1  2007]  28\ Weeks\ Later\ [2007].divx

EDIT ---

To get this answer to work correctly on an external ExFAT drive, a HFS+ disk image had to be created to give the correct permissions. The disk image was created on the external drive and the script was run from that disk image successfully