WR7500
WR7500

Reputation: 449

How to specify multiple file types to exclude when deleting files in Powershell

I'm using the below script to copy files from an install directory, and then remove all files except XML files. I'd like to also exclude .log and .txt files from deletion. How do I format this to accomplish that?

Bonus points if there is a way to download ONLY XML/LOG/TXT files, including the folder structure, without having to go back and delete extra stuff.

Copy-Item -Path $cis_wild -Destination $target_cis -Container -Recurse
Get-ChildItem -Path $target_cis -Recurse -File | Where {($_.Extension -ne ".xml")} | Remove-Item

Upvotes: 2

Views: 2301

Answers (1)

mklement0
mklement0

Reputation: 439597

Use the -Exclude parameter, which allows you to specify an array of exclusion wildcard patterns - but see the caveats below:

Get-ChildItem -Exclude *.xml, *.txt, *.log -Path $target_cis -Recurse -File | 
  Remove-Item -WhatIf

Note: The -WhatIf common parameter in the command above previews the operation. Remove -WhatIf once you're sure the operation will do what you want.


Post-filtering solution, building on your own attempt:

While it is generally preferable to solve a problem directly with a given cmdlet's parameter - both for concision and performance - as of PowerShell 7.2.2, there are good reasons to perform the filtering separately, applying it after Get-ChildItem has returned its (unfiltered) results (which is what you attempted):

  • While -Include / -Exclude work as expected with Get-ChildItem -Recurse, they don't without it, and it's not easy to remember this fact - see the bottom section.

  • Combining -Include / -Exclude with Get-ChildItem -Recurse is unexpectedly slow - even slower than a post-filtering solution: see GitHub issue #8662

Get-ChildItem -Path $target_cis -Recurse -File | 
  Where-Object { $_.Extension -notin '.xml', '.txt', '.log' } |
    Remove-Item -WhatIf

That is, switching from the -ne operator in your attempt ($_.Extension -ne ".xml") to the
-notin operator allows placing an array of strings on the RHS to test the the LHS string against:

$_.Extension -notin '.xml', '.txt', '.log' returns $true if the value of $_.Extension is not present in the RHS array (using implicit -eq testing against each element).


Caveats as of PowerShell 7.2.2:

  • It is solely because your commands use -Recurse for recursive traversal with Get-ChildItem that -Exclude and -Include work as one would expect; without -Recurse, these parameters exhibit counter-intuitive behavior - see this answer.

  • With Copy-Item, even -Recurse wouldn't make -Include work, though -Exclude would mostly work as expected:

    • That is, while you should be able to use -Include with multiple wildcard patterns for inclusionary matching directly with Copy-Item -Recurse in order to limit what is copied to begin with, it doesn't work.

    • -Include is mistakenly applied to the immediate target directory and only to it:

      • If none of the specified patterns match its name, nothing is copied.
      • If any specified pattern does match its name, the whole directory subtree is copied without filtering.
    • -Exclude, by contrast, mostly works as expected: it applies the exclusion patterns on each level of the directory subtree hierarchy.

      • If any exclusion pattern happens to match the name of the immediate target directory, nothing is copied.
    • See this answer for a detailed discussion, including a problem with how -Destination arguments are interpreted.

Upvotes: 4

Related Questions