Search code examples
powershellforeachorganizationpowershell-7.0

Powershell loop is only running once file per filename, even if the filename exists with multiple extensions


I'll be the first to admit that PowerShell isn't my strong suit, but I've pieced together the following after an evening of digging around on the internet. The end goal is to organize a huge drive of images by the DateTaken, as well as sidecar XMP files if they exist. It's probably not the most elegant code, but it almost works.

The last issue, which I can't figure out, is that the foreach loop only executes once per filename, regardless of extension. For example, only DSC00001.arw, DSC00001.xmp, or DSC00001.jpg would be processed.

Any points in the right direction would be appreciated.

Thanks!

$Folders = (Get-ChildItem -Path F:\ -Recurse -Directory -Force).FullName

$objShell  = New-Object -ComObject Shell.Application
$Folders | % {

    $objFolder = $objShell.namespace($_)
    foreach ($File in $objFolder.items()) { 
            if (($File | Split-Path -Extension) -in ".arw",".gif",".tiff",".jpg",".png",".nef") {
                Write-Host $File.Name`t -NoNewline -ForegroundColor Green
                try {
                    $DateTaken = ($objFolder.getDetailsOf($File,12) -replace [char]8206)  -replace [char]8207
                    $DateTaken = [DateTime]::ParseExact($DateTaken, "g", $null)
                    $Year = $DateTaken.ToString('yyyy')
                    $Date = $DateTaken.ToString('yyyy-MM-dd')
                    Write-Host $Date`t -ForegroundColor Blue -NoNewline
                }
                catch {
                    $Year = 'Other'
                    $Date = 'Other'
                    Write-Host $Date`t -ForegroundColor DarkRed -NoNewline
                }
                finally {
                    $DatePath = (Join-Path (Join-Path F:\ $Year ) $Date)
                }
                write-Host $File.Path -> (Join-Path $DatePath ($File | Split-Path -Leaf))-NoNewline
                #Move-Item $File.Path (Join-Path $DatePath ($File | Split-Path -Leaf))
                Write-Host Completed`n -NoNewline

                if (Test-Path (Join-Path ($File | Split-Path) (($File | Split-Path -LeafBase) + '.xmp'))) {
                    Write-Host XMP:`t -ForegroundColor Magenta -NoNewLine
                    Write-Host (Join-Path ($File | Split-Path) (($File | Split-Path -LeafBase) + '.xmp')) -> (Join-Path ($DatePath) (($File | Split-Path -LeafBase) + '.xmp'))
                    #Move-Item (Join-Path ($File | Split-Path) (($File | Split-Path -LeafBase) + '.xmp')) (Join-Path ($DatePath) (($File | Split-Path -LeafBase) + '.xmp'))
                }
            }else {
                Write-Host $File.Name is not an image `n -NoNewline -ForegroundColor DarkYellow    
            }
    }

}

Solution

  • I'm not sure why $objFolder.items() doesn't actually return all expected files, but I would suggest using PowerShell's built-in file system provider to discover the actual files in each folder and then use $objFolder.ParseName() to obtain a reference you can pass to GetDetailsOf():

    $Folders = (Get-ChildItem -Path F:\ -Recurse -Directory -Force).FullName
    
    $objShell  = New-Object -ComObject Shell.Application
    $Folders | % {
    
        $objFolder = $objShell.NameSpace($_)
    
        foreach ($FileInfo in Get-ChildItem -LiteralPath $_ -File -Force) { # <-- trick is right here, use Get-ChildItem to discover the files in the folder
            if($FileInfo.Extension -in ".arw",".gif",".tiff",".jpg",".png",".nef"){
                $File = $objFolder.ParseName($FileInfo.Name)                # <-- here we resolve the corresponding file item with shell app
        
                # ... resolve and parse dateTaken here, just like before
    
                # *.xmp file path might be easier to construct like this
                $xmpPath = $FileInfo.FullName -replace "$($FileInfo.Extension)`$",'.xmp'
                if(Test-Path -LiteralPath $xmpPath){
                     # ...
                }
            }
        }
    }