Move-Item, but keep newest if conflict

Viewed 216

I got a few foreach loops, where I am doing some if and elseif's in. They're basically set to sort out a bunch of files based on their name. But I got a particular case, where there are a few files, that aparantly exist in 2 different locations. But I want to keep the newest file always.

My code looks like this

# Variable for destination folder
$CreoRod = "C:\Test\Creo"
# Variable for source folder
$97MappeRod = "C:\Test\97"
# Variable to get each item in source folder
$97Files = Get-ChildItem -File -Path $97MappeRod
# Variable to get each item in destination folder
$CreoRodFiles = Get-ChildItem -File -Path $CreoRod

# Foreach loop to move files from source folder to corresponding destination folder 
foreach ($file in $97Files) {
    if ($file -like "0*") {
        Move-Item -Path "$97MappeRod\$file" -Destination "$CreoRod\0xxx"
    } elseif ($file -like "48*") {
        Move-Item -Path "$97MappeRod\$file" -Destination "$CreoRod\48xxx"
    } elseif ($file -like "49*") {
        Move-Item -Path "$97MappeRod\$file" -Destination "$CreoRod\49xxx"
    } elseif ($file -like "97*") {
        Move-Item -Path "$97MappeRod\$file" -Destination "$CreoRod\97xxx"
    } elseif ($file -like "98*") {
        Move-Item -Path "$97MappeRod\$file" -Destination "$CreoRod\98xxx"
    } elseif ($file -like "99*") {
        Move-Item -Path "$97MappeRod\$file" -Destination "$CreoRod\99xxx"
    } elseif ($file -notmatch "config.pro") {
        Move-Item -Path "$97MappeRod\$file" -Destination "$CreoRod\Diverse"
    }
}

Is it possible to do something to make it compare with existing file, and if the existing file is newer, then delete the current. But if it is older then replace?

2 Answers

I haven't tested this but it might help you with the logic, first you can use a helper function to test if a file with the same name exists in the destination and if it does, test if it should be replaced ($true if newer or if the destination does not have a file with that name) or remove the source file ($false).

For the multiple if conditions, I would recommend you to replace them for a switch statement which gives more readability.

function ShouldMove {
    param($File, $Destination)
    $testPath = Join-Path $Destination -ChildPath $File.Name
    if(Test-Path $testPath) {
        # if the destination folder contains this file name
        $destFile = Get-Item $testPath
        if($File.CreationTime -gt $destFile.CreationTime) {
            # if the source file is newer return `$true`
            return $true
        }
        # else `$false`
        return $false
    }
    # if destination does not have this file, return `$true`
    $true

}

foreach ($file in $97Files) {
    $destination = switch -Wildcard($file.Name) {
        "0*"  { "$CreoRod\0xxx" ; break }
        "48*" { "$CreoRod\48xxx"; break }
        "49*" { "$CreoRod\49xxx"; break }
        "97*" { "$CreoRod\97xxx"; break }
        "98*" { "$CreoRod\98xxx"; break }
        "99*" { "$CreoRod\99xxx"; break }
        { $_ -notmatch "config.pro" } { "$CreoRod\Diverse" }
        Default {
            Write-Warning "No condition was met, ignoring $($file.Name)"
        }
    }
    
    if(-not $destination) { continue }
    if(ShouldMove -File $file -Destination $destination) {
        # `-Force` to replace it
        Move-Item -LiteralPath $file.FullName -Destination $destination -Force
        continue
    }
    # if `ShouldMove` returned `$false` then, instead of moving
    # the file, delete the source
    Remove-Item -LiteralPath $file.FullName
}

I think your question is about moving file to a new destination folder if the file name meets certain conditions.
Then, if a file with that name already exists in the target destination folder, move that file only if it is newer than the existing file.

If that assumption is correct, you can do:

$CreoRod    = "C:\Test\Creo"   # destination
$97MappeRod = "C:\Test\97"     # source
$97Files     = Get-ChildItem -File -Path $97MappeRod

foreach ($file in $97Files) {
    # determine destination path
    $targetFolder = $null
    if ($file.Name -match '^(0|48|49|97|98|99)') { $targetFolder = '{0}xxx' -f $matches[1] }
    elseif ($file.Name -notlike '*config.pro*')  { $targetFolder = 'Diverse' }

    # file name was not recognized, so skip this one and proceed with the rest
    if ([string]::IsNullOrWhiteSpace($targetFolder)) { continue }

    # complete the target destination path
    $targetFolder = Join-Path -Path $CreoRod -ChildPath $targetFolder

    # try and get the file already in the targetFolder
    $existingFile = Get-Item -Path (Join-Path -Path $targetFolder -ChildPath $file.Name) -ErrorAction SilentlyContinue
    if ($existingFile) {
        # a file with that name already exists, check if the file from the source folder is newer
        $canMoveThis = ($existingFile.LastWriteTime -lt $file.LastWriteTime)
    }
    else {
        $canMoveThis = $true
        # should we create the target path if it doesn't exist yet?
        $null = New-Item -Path $targetFolder -ItemType Directory -Force
    }

    if ($canMoveThis) {
        # all clear, move the file
        $file | Move-Item -Destination $targetFolder -Force
    }
}
Related