2

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
WR7500
  • 417
  • 1
  • 3
  • 12
  • 2
    [1] if the `-Exclude` parameter of `Get-ChildItem` was reliable, i would say to use that. it aint, tho. ///// [2] instead, use a regex OR pattern of your extensions to filter the files. – Lee_Dailey Apr 22 '22 at 22:29
  • I agree, `-Exclude` and `-Include` scare me :( I would rather use a manual approach – Santiago Squarzon Apr 22 '22 at 22:43
  • @Lee_Dailey, the rule of thumb is: _with `-Recurse`_, `-Exclude` and `-Include` work as expected (I know it isn't easy to remember these subtleties). – mklement0 Apr 22 '22 at 22:46
  • 2
    @mklement0 - from my direct experience ... the `-Recurse` is needed ... but the code is still flaky. i have had it get only SOME of the targets - and that aint acceptable. i finally decided that - since both `-Include` and `-Exclude` are run after the filesystem returns things to the cmdlet - i would use `ForEach-Object` and save me the pain of wondering if it wold fail this time. [*sigh ...*] it aint an all-up `filter left`, but it works every time. [*grin*] – Lee_Dailey Apr 22 '22 at 23:09
  • again, agree with Lee hehe, would trust more my own code than having to remember when the params are _supposed to work_ and even then wonder if they will work. – Santiago Squarzon Apr 22 '22 at 23:13
  • @Lee_Dailey, that's understandable - it is annoying that you can't just rely on these parameters to work consistently. Yes, filtering happens after the fact, but the aspect of using just a parameter on the original cmdlet vs. having to filter via `ForEach-Object` in a separate pipeline segment is still appealing. I'm not aware of any cases where combination with `-Recurse` fails - if you can summarize succinctly the pattern of when it does, please do. Additionally - assuming PowerShell (Core) is still affected - I encourage you to file a bug report. – mklement0 Apr 23 '22 at 03:55
  • 1
    @mklement0 - i never was able to nail it down. [*blush*] what seemed to be the same situation would _sometimes_ ignore those two parameters. i just gave up and let it go ... filtering in the next pipeline stage works _all the time_ for me. – Lee_Dailey Apr 23 '22 at 13:38
  • Understood, @Lee_Dailey - I've added more details on what does and doesn't work to the answer. I can definitely see why you would avoid these parameters, but perhaps spelling out all the "quirks" publicly here helps get them fixed, eventually. – mklement0 Apr 25 '22 at 15:40
  • 1
    @mklement0 - thank you for the added info. it helps make things a good bit more clear ... [*grin*] – Lee_Dailey Apr 25 '22 at 22:01

1 Answers1

4

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.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Thanks, @WR7500; actually, `-Exclude` (mostly) does work as expected with `Copy-Item -Recurse`, but `-Include` is fundamentally broken - please see my update. – mklement0 Apr 25 '22 at 15:36