Powershell script to move files into year/month folders based on creation timestamp

18,302

Solution 1

So after spending a while pouring over a number of pages filled with scripts, I came up with this solution below and modified to fit my needs. I have it employed into production and it works great.

Adapted from PowerShell: Moving files into subfolder based on date

<#
Set Variables of Source folder and Destination folder
Assign variable of files to be the files with uss extension
For each file with uss extension assign the Directory variable the information for file creation year and month
    if the year and month folder do not exist, then create them from file creation information
Move file to sub-folder of year and month from file creation information passed into Directory variable
#>

$SourceDir = "path to uss files\USS_Files\"
$DestinationDir = "path to uss files\USS_Files\"

$files = get-childitem $SourceDir *.uss

foreach ($file in $files) 
{
    $Directory = $DestinationDir + "" + $file.CreationTime.Date.ToString('yyyy') + "\" + $file.CreationTime.Date.ToString('MM-MMM')

    if (!(Test-Path $Directory))
    {
        New-Item $directory -type directory
    }
    Move-Item $file.fullname $Directory 
}

Solution 2

Assuming that you are using this to arrange photos into folders, and that your file names start with YYYYMMDD, then the following script works well. This method was preferred to the method above because we copied/pasted/moved and hence the creation and modified dates were not the same as the dates taken.

Also, to run this script, just paste into a text file with an extension .ps1, right click on the file, and select run with powershell.

# Get the files which should be moved, without folders
$files = Get-ChildItem 'C:\Users\YourName\SourceFolder\YourPhotosFolder' -Recurse | where {!$_.PsIsContainer}

# List Files which will be moved
$files

# Target Filder where files should be moved to. The script will automatically create a folder for the year and month.
$targetPath = 'C:\Users\YourName\SourceFolder\YourAlbumnsFolder'

foreach ($file in $files)
{
# Get year and Month of the file using the filename
$bn = $file.basename.ToString()
$year = $bn.substring(0,4)
$month = $bn.substring(4,2)
$day = $bn.substring(6,2)

# Out FileName, year and month
$file.Name
$year
$month
$day

# Set Directory Path
$Directory = $targetPath + "\" + $year + "\" + $day + "-" + $month + "-" + $year
# Create directory if it doesn't exsist
if (!(Test-Path $Directory))
{
New-Item $directory -type directory
}

# Move File to new location
$file | Move-Item -Destination $Directory
}

If you want to use the last modified date then use the following (slightly modified version of the previous code)

# Get the files which should be moved, without folders
$files = Get-ChildItem 'C:\Users\YourName\SourceFolder\YourPhotosFolder' -Recurse | where {!$_.PsIsContainer}

# List Files which will be moved
$files

# Target Filder where files should be moved to. The script will automatically create a folder for the year and month.
$targetPath = 'C:\Users\YourName\SourceFolder\YourAlbumnsFolder'

foreach ($file in $files)
{
# Get year and Month of the file
# I used LastWriteTime since this are synced files and the creation day will be the date when it was synced
$year = $file.LastWriteTime.Year.ToString()
$month = $file.LastWriteTime.Month.ToString()
$day = $file.LastWriteTime.Day.ToString()

# Out FileName, year and month
$file.Name
$year
$month
$day

# Set Directory Path
$Directory = $targetPath + "\" + $year + "\" + $day + "-" + $month + "-" + $year
# Create directory if it doesn't exsist
if (!(Test-Path $Directory))
{
New-Item $directory -type directory
}

# Move File to new location
$file | Move-Item -Destination $Directory
}

If you want to use the created date then use the following (slightly modified version of the previous code)

# Get the files which should be moved, without folders
$files = Get-ChildItem 'C:\Users\YourName\SourceFolder\YourPhotosFolder' -Recurse | where {!$_.PsIsContainer}

# List Files which will be moved
$files

# Target Filder where files should be moved to. The script will automatically create a folder for the year and month.
$targetPath = 'C:\Users\YourName\SourceFolder\YourAlbumnsFolder'

foreach ($file in $files)
{
# Get year and Month of the file
# I used LastWriteTime since this are synced files and the creation day will be the date when it was synced
$year = $file.CreationTime.Year.ToString()
$month = $file.CreationTime.Month.ToString()
$day = $file.CreationTime.Day.ToString()
$hour = $file.CreationTime.Hour.ToString()

# Out FileName, year and month
$file.Name
$year
$month
$day

# Set Directory Path
$Directory = $targetPath + "\" + $year + "\" + $day + "-" + $month + "-" + $year
# Create directory if it doesn't exsist
if (!(Test-Path $Directory))
{
New-Item $directory -type directory
}

# Move File to new location
$file | Move-Item -Destination $Directory
}

Entire project code on GitHub

Solution 3

Like this?

$USS_Folder = "path\USS_Files"

get-childitem | % {

    $file = $_.FullName 
    $date = Get-Date ($_.CreationTime)
    $month = $date.month
    $year = $date.year

    new-item -type Directory -path "$USS_Folder\$year\$month"
    move-item $file "$USS_Folder\$year\$month"
}

Solution 4

Here is a solution I used in my environment but converted to your requirements:

 $USS_Folder = "path\USS_Files\"
 Get-ChildItem -File| Sort-Object LastWriteTime |`
  Group {$_.LastWriteTime.ToString("yyyy-MM")} |`
  % {
    $folder = $USS_Folder + $_.name 
    if (!(Test-Path $folder)) `
      {new-item -type Directory -path $folder -ErrorAction SilentlyContinue}
    $_.group|move-item -Destination $folder
    }
Share:
18,302

Related videos on Youtube

Joshua
Author by

Joshua

Been a hardware technician and windows person for a while now...moving into development and Linux stuff since it is really fun. Please forgive me while I learn how to use this site...I will remove this later when I have confidence I am doing it right. LOL!!!

Updated on June 25, 2022

Comments

  • Joshua
    Joshua almost 2 years

    First post here...I apologize if this isnt formatted correctly...I will work on this. I am working on a Powershell script that will get me the following information so that I can work with another batch file that works perfectly to this point. As I grow in my understanding of Powershell...I will most likely change the batch file since it is pretty lengthy.

    TL:DR Newb working with Powershell needs help

    I am wanting Powershell to output a single line of information for each file in a folder excluding any subfolders. I would like my txt file to look like this:

    file creation date_full path to filename

    one file per line. This will be parsed into a text file later

    Here is what I have so far...seems like I just need a for loop to run something like this psuedocode and I should be good to go. Any help would be appreciated at this point.

    Thanks all and I hope I am not killin you with formatting.

    $USS_Folder="path\USS_Files\"
    $uss_year=#4 digit year of file creation date
    $uss_month=#2 digit year of file creation date
    $uss_file=# Full filename including path and Creation_Date
    
    New-Item -ItemType directory -Path $USS_Folder\$uss_year\$uss_month
    
    Move-Item $uss_file $USS_Folder\$uss_year\$uss_month
    
  • sl0815
    sl0815 about 10 years
    The test-path is really necessary if you have thousands of files. Thanks!
  • DiegoDD
    DiegoDD almost 6 years
    Sorry to revive this after so long... I made a few edits to this for my needs (getting al files in all subfolders, and of any extension) and works perfectly, but I have problems when filenames contain [or ] in the name. How can I escape such characters so the script doesn't fail for them? I have NO experience with powershell so please be very specific. Thanks!
  • Kiquenet
    Kiquenet almost 4 years
    @sl0815 necessary if you have thousands of files ? why?
  • sl0815
    sl0815 almost 4 years
    @Kiquenet because then the script does not try to create a dir that might already exist (like other solutions listed here). This speeds up things considerably. In most cases only a few dirs need to be created (which was the whole point for me) and then most calls to new-item will be skipped.