Order in the photo archive using powershell

Hello, Habr!



The other day, a typical task arose - to help a friend turn a mountain of photos into an ordered hierarchy. Everything would be fine, but there are not one mountains of photos, but two - on a Mac and on a laptop under Win10. In search of a solution, I came across several scripts for linux , but I could not find something of such a cross-platform. We will write for ourselves - please, under cat.



So, we need to automate something, including on Windows. We say automation, we mean Powershell - and to my great happiness, Microsoft recently made it cross-platform. We will use it.



Since photographs have an obligatory common attribute, creation date, we will build a hierarchy on its basis in the following order: archive root β†’ year β†’ month β†’ day. In the course of action:



  1. Create the root directory
  2. Get the list of files
  3. For each file, determine the creation date from EXIF ​​and

    • We supplement the directory structure, if necessary,
    • Drag the file.


It sounds easy, let's go.



Since the user is unqualified, it is logical to save him as much as possible from working in the terminal. Let the script act in its directory and subfolders, the user only needs to put it in the root folder and then drag it to the powershell terminal window:



[CmdletBinding()] $curDir=$MyInvocation.MyCommand.Path | Split-Path -Parent
      
      





We check the availability of exiftool.



 if (!(ExifTool)) { Write-Host "Install exiftool first!" -ForegroundColor Red -BackgroundColor Black break }
      
      





We denote the extensions of interest to us and get a list of files with which we will work. We select only those that have the necessary extension and are not yet sorted.



Create the root of our future structure:



 $ExtList = "arw,jpg,jpeg,NEF" $FileList = Get-ChildItem $curDir -Recurse | ` Where-Object {(($ExtList.ToUpper() -match $_.Name.Split('.')[-1]) ` -or ($ExtList.ToLower() -match $_.Name.Split('.')[-1])) ` -and ($_.FullName -notmatch "Ordered") } # Define & reate archive root folder $ArchiveRoot = Join-Path -Path $curDir -ChildPath "Ordered" if (!(Test-Path $ArchiveRoot)) { New-Item -ItemType Directory -Path $ArchiveRoot }
      
      





Since not all jpeg files can have the necessary information in EXIF ​​(for example, after re-saving from social networks), we create a separate directory for them. The user will decide their fate separately:



 $ChaosRoot = Join-Path -Path $curDir -ChildPath "Chaos" if (!(Test-Path $ChaosRoot)) { New-Item -ItemType Directory -Path $ChaosRoot }
      
      





For each file of those that we parse, do the following.



We determine the creation date using exiftool and break it into three components: year, month, day. Since exiftool supports export to csv, and powershell can work with csv:



 foreach ($File in $FileList) { $FileDateString = $null $FileDate = $null try { $FileDateString = ConvertFrom-Csv (ExifTool.exe $File.FullName -CSV) | ` Select-Object -ExpandProperty CreateDate -ErrorAction Stop } catch { Move-Item -Path $File.FullName -Destination $ChaosRoot -Force continue } $FileDate = $FileDateString.Split(" ")[0].Split(":")
      
      





If it was possible to get the desired attribute, then we break it into separate values ​​of the year, day and month and go further, and if not, no, the unidentified file immediately flies into chaos.



So, the date is received. Create a directory, and drag the file:



 $YearPath = Join-Path $ArchiveRoot $FileDate[0] $MonthPath = Join-Path $YearPath $FileDate[1] $FileDest = Join-Path $MonthPath $FileDate[2] if (!(Test-Path $FileDest)) { New-Item -ItemType Directory -Path $FileDest } Move-Item -Path $File.FullName -Destination $FileDest -Force }
      
      





Well, do not forget to clean up the trash by deleting the already unnecessary empty directories:



 $AllDirsAtFinal = Get-ChildItem -Path $curDir -Recurse -Force | Where-Object {$_.PSIsContainer -eq $true} foreach ($Dir in $AllDirsAtFinal) { $DirLenght = Get-ChildItem -Path $Dir.FullName -Recurse -Force -ErrorAction SilentlyContinue | ` Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue if ($null -eq $DirLenght.Sum) { Remove-Item $Dir.FullName -Recurse -Force -ErrorAction SilentlyContinue } }
      
      





That, in general, is all.



For work you will need:



PowerShell for Mac

ExifTool by Phil Harvey



All Articles