Michiel
Michiel

Reputation: 51

remove items, except folders in an array

So, i've been scratching my head for a while now and can't seem to figure it out.

I want to delete files and folders older than 'x' days <-- this works fine I want to delete empty directories left behind <-- this works fine as well

I also want to have some exceptions: filenames and foldernames. The filename exception works fine, but folders don't. There is something strange though. If i put only 1 name in the array of folders i don't want to delete, it works just fine. But if i put multiple in, it suddenly doesn't work anymore?

I have the idea it might be something simple i'm completely missing

$limit = (Get-Date).AddDays(-120)
$path = "C:\Users\user\Documents\files"
$ExcludedFileNames = @("*file1*", "*file2*")
$ExcludedFolders = @("*folder1*", "*folder2*")

# Delete files older than the $limit.
Get-ChildItem -Path $path -Recurse -Force -exclude $ExcludedFileNames |
Where-Object {($_.FullName -notlike $ExcludedFolders) -and (!$_.PSIsContainer) -and ($_.LastWriteTime -lt $limit) } |
Remove-Item -Force

# Delete any empty directories left behind after deleting the old files.
Get-ChildItem -Path $path -Recurse -Force | Where-Object { $_.PSIsContainer -and (Get-ChildItem -Path $_.FullName -Recurse -Force | Where-Object { !$_.PSIsContainer }) -eq $null } | Remove-Item -Force -Recurse

Instead of $.FullName i tried $.Name Instead of -notlike i tried -notin I also tried removing the array and put the variables after where-object

I also tried to copy other code from lots of posts but didn't seem to help.

Upvotes: 2

Views: 278

Answers (2)

Theo
Theo

Reputation: 61093

I would use wildcards on the file names to use with the -Exclude parameter, and create a regex string for the foldernames to exclude you can use in the Where-Object clause.

Something like this:

$limit = (Get-Date).AddDays(-120).Date      # set to midnight instead of the current time
$path  = 'C:\Users\user\Documents\files'
$ExcludedFileNames = '*file1*', '*file2*'   # wildcards for the Exclude parameter
$ExcludedFolders   = 'folder1','folder2'    # can be a partial name, do not use wildcards here

# create a regex string for the folder names to exclude
# each item will be Regex Escaped and joined together with the OR symbol '|'
$FoldersToSkip = ($ExcludedFolders | ForEach-Object { [Regex]::Escape($_) }) -join '|'

# Delete files older than the $limit.
Get-ChildItem -Path $path -File -Recurse -Force -Exclude $ExcludedFileNames |
Where-Object {($_.DirectoryName -notmatch $FoldersToSkip) -and ($_.LastWriteTime -lt $limit) } |
Remove-Item -Force

# Delete any empty directories left behind after deleting the old files.
(Get-ChildItem -Path $path -Recurse -Directory -Force).FullName |
Where-Object { !( Get-ChildItem -Path $_ | Select-Object -First 1 ) } |
Sort-Object -Property Length -Descending |
Remove-Item -Force

Upvotes: 3

Mathias R. Jessen
Mathias R. Jessen

Reputation: 174545

The problem is that -notlike expects a single string as it's right-hand side operand, and so the $ExcludedFolders variable is coerced into the stringvalue "*folder1* *folder2*".

The comparison 'C:\some\path\to\a\folder1\with\a\file.exe' -notlike '*folder1* *folder2*' obviously fails.

You can solve this by using the -notmatch regex operator instead:

$ExcludedFolders = @('folder1', 'folder2') # note that we no longer need the wildcards

# later
... |Where-Object {$_.FullName -notmatch ($ExcludedFolders.ForEach{[regex]::Escape($_)} -join '|') -and (-not $_.PsIsContainer) -and $_.LastWriteTime -lt $limit}

The | is the alternation operator in regex, effectively functioning as an OR

Upvotes: 4

Related Questions