1

I have a Powershell script with If statement and multiple conditions. My code is working great but I am looking for to display which condition my object doesn't respect.

Get-ChildItem $Path -Directory -Force | ForEach-Object {

$FolderName = $_.BaseName -match $Folderpattern
$DateOK = $_.LastWriteTime -lt (Get-Date).AddDays(-3))
$Folder = $_.BaseName
    
if (($FolderName) -and ($DateOK) {
                write-host("$Folder can be moved") 
            }
            else {
                write-host("$Folder can't be moved")
            }
        }

I would like to display "$folder can't be moved because it doesn't match the pattern" if it doesn't respect the $FolderName condition.

And display "$folder can't be moved because the last write time is less than 3 days" if it doesn't respect the $DateOK condition.

thanks for the help

3
  • 1
    What should it say if both conditions fail? Commented May 18, 2022 at 15:14
  • There's a lot of ways to approach this solution (e.g. we could suggest switch with a script block); but the cleanest from a code perspective (IMO) is to create a custom function to replace your ForEach loop, have it accept pipeline input, then in the process block include guard clauses (see foxdeploy.com/blog/… for some examples - only use return instead of throw given the context is a loop) Commented May 18, 2022 at 15:17
  • You're missing a parenthesis in the if statement. Commented May 18, 2022 at 18:22

2 Answers 2

3

If this is just for checking and not for keeping a log where you need those specific messages I might go for something simple where we just capture the true and false values for each of the tests.

$path = C:\temp
Get-ChildItem $Path -Directory -Force |
    ForEach-Object {
        [PSCustomObject]@{
            Folder         = $_.BaseName
            LastWriteTime  = $_.LastWriteTime
            FolderNameTest = $_.BaseName -match 'test'
            DateOKTest     = $_.LastWriteTime -lt (Get-Date).AddDays(-30)
        }
    }

Sample Output

Folder         LastWriteTime       FolderNameTest DateOKTest
------         -------------       -------------- ----------
.git           06.09.2021 01:06:06          False       True
.vscode        25.09.2021 10:06:11          False       True
1              22.09.2021 22:30:26          False       True
batch_test     02.05.2022 22:29:25           True      False
cleanup        20.09.2021 10:02:51          False       True
DeviceDatabase 26.09.2021 12:07:26          False       True
host           22.09.2021 23:23:38          False       True
move_logs      26.04.2022 19:28:59          False      False
test_run       01.03.2022 22:14:14           True       True

You can then pipe this to Export-Csv if you like

Sign up to request clarification or add additional context in comments.

Comments

2

There are various ways to go about this; but this one is clean and easy to understand, so would be my preferred route:


Function Move-FolderConditional { # todo: give this cmdlet a better name for your context
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.IO.DirectoryInfo[]]$Path
        ,
        [Parameter(Mandatory)]
        [System.IO.DirectoryInfo]$Destination
        ,
        # files matching this pattern get moved to the target
        [Parameter(Mandatory)]
        [string]$ArchivableFolderPattern
        ,
        # Files older than this date get moved to the target
        [Parameter()]
        [string]$MinKeepDateUtc = (Get-Date).AddDays(-3).ToUniversalTime()
    )
    Process {
        foreach ($directory in $Path) {
            if ($directory.BaseName -notmatch $ArchivableFolderPattern) {
                Write-Warning "Could not move folder '$($directory.FullName)' as the name does not match the required pattern"
                continue;
            }
            if ($directory.LastWriteTimeUtc -ge $MinKeepDateUtc) {
                Write-Warning "Could not archive folder '$($directory.FullName)' as it was last updated at '$($directory.LastWriteTimeUtc.ToString('u'))'"
                continue;
            }
            try {
                #Move-Item -Path $directory -Destination $Destination -ErrorAction Stop # Uncommend this if you actually want to move your files
                Write-Information "Successfully moved '$($directory.FullName)' to '$($Destination.FullName)'" 
            } catch [System.Management.Automation.ItemNotFoundException] { # For this exception we'd probably check in the Begin block instead - but this is just to give the idea that we could add a try/catch if required
                Write-Warning "Could not archive folder '$($directory.FullName)' the target directory does not exist: '$($Destination.FullName)'"
            }  
        }
    }
}

# Example usage
Get-ChildItem $path -Directory -Force | Move-FolderConditional -ArchivableFolderPattern '^log' -InformationAction Continue -Destination 'z:\archive\'

But other options are available (I've just included snippets to give the gist of these):

Switch Statement

switch ( $directory )
{
    {$_.BaseName -notmatch $ArchivableFolderPattern}
    {
        Write-Warning "Could not move folder '$($_.FullName)' as the name does not match the required pattern"
        break
    }
    {$_.LastWriteTimeUtc -ge $MinKeepDateUtc}
    {
        Write-Warning "Could not archive folder '$($_.FullName)' as it was last updated at '$($_.LastWriteTimeUtc.ToString('u'))'"
        break
    }
    default 
    {
        Write-Information "Successfully moved '$($_.FullName)' to '$($Destination.FullName)'" 
    }
}

Flags

[bool]$archiveFolder = $true
if ($directory.BaseName -notmatch $ArchivableFolderPattern) {
    Write-Warning "Could not move folder '$($directory.FullName)' as the name does not match the required pattern"
    $archiveFolder = $false
}
if ($directory.LastWriteTimeUtc -ge $MinKeepDateUtc) { 
    # note: this will process even if archivefolder is already false... you can use `else` or amend the condition to include `$archiveFolder -or ($directory.LastWriteTimeUtc -ge $MinKeepDateUtc)`; though if going that route it's better to use the switch statement.
    Write-Warning "Could not archive folder '$($directory.FullName)' as it was last updated at '$($_.LastWriteTimeUtc.ToString('u'))'"
    $archiveFolder = $false
}
if ($archiveFolder) {
    Write-Information "Successfully moved '$($directory.FullName)' to '$($Destination.FullName)'" 
}

Other

Or you can do combinations of the above (e.g. use the switch statement to set your flags (in which case you can optionally remove the break so that all issues are displayed).

4 Comments

The switch looks very clean, especially if you want to output all issues.
Indeed - though the switch above wouldn't output all issues - to do that you'd have to replace break with $archiveFolder=$false / mix in some of the other logic from the Flags example.
In PowerShell, switch enters all matching "case" branches, unless there is a break, so just removing the break should output all issues.
@zett42 ah good point; I was thinking it would still process the default block; but you're right; if 1 or more cases are matched the default isn't used regardless of breaks (it's not a feature I use often, so had to test that to check). Thanks for correcting me :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.