1

The Setup

I have a folder structure that looks like:

C:\RootFolder
            \file1.txt
            \file2.txt
            \license.txt
            \Settings
                     \fileA.txt
                     \fileB.txt
                     \settings.txt
            \OtherFolders

The Goal

Delete all of the files except for license.txt and settings.txt.

I would like in the end for only the following to remain:

  C:\RootFolder
                \license.txt
                \Settings
                         \settings.txt

The Script to Far

$exclude = @('license.txt', 'settings.txt')
Get-ChildItem C:\RootFolder -recurse -exclude $exclude | foreach ($_) {remove-item $_.fullname -recurse:$false}

The Problem

Even though I specify -recurse:$false specifically, it always generates a message for each folder indicating that "recurse isn't specified" and saying it will delete all child items.

After this, the license.txt file remains, but the settings.txt file (in a subdirectory) does not.

2 Answers 2

3

And almost as soon as I posted, the answer came to me -- I had to find only files, not directories.

Since I'm on PowerShell 2.0, I couldn't use the -File attribute that's new in powershell 3. Instead, I have to check every object to see if it's a container.

Solution:

$exclude = @('license.txt', 'settings.txt')

Get-ChildItem C:\RootFolder -recurse -exclude $exclude | Where-Object {!($_.PSIsContainer)} | foreach ($_) {remove-item $_.fullname}

This worked perfectly to achieve what I needed.

Improving on the Solution

Thanks to several comments / posts here, there are some more elegant ways that taught me a bit more about powershell:

  • Remove-Item can take commands from a pipe-line. No foreach is necessary.
  • Where-Object can just be shortened to "?"
  • Get-ChildItem can be shortened to "gci" (alias)
  • We don't need a ($_) after the Foreach. Foreach understands that automatically.
  • If we wanted to put the excludes on one line, we can do it via an array right inline

Given these, an elegant solution that fits into a one-liner would be:

gci C:\RootFolder -Recurse -Exclude @('license.txt', 'settings.txt') | ? { ! $_.PSIsContainer } | Remove-Item -Force
Sign up to request clarification or add additional context in comments.

1 Comment

Remove-Item can read from a pipe, so you can simplify that statement to gci C:\RootFolder -Recurse -Exclude $exclude | ? { ! $_.PSIsContainer } | Remove-Item -Force.
1

You code works great.

Just a one-liner so we can do it another way:

Get-ChildItem C:\RootFolder -recurse | Where-Object {!($_.PSIsContainer)} | Foreach { if ( $_.Name -ne "license.txt" -and $_.Name -ne "settings.txt") {remove-item $_.FullName -force}}

If there are a lot of files need to be excluded then this one liner doesn't scale - in that case I will use your code:)

@Sean Killeen by the way: Is there a ($_) after foreach? Sounds like you are using Foreach-Object Cmdlet instead of Foreach construct. My understanding is for Cmdlet we don't need ($something in $sometingElse) after "foreach-object". But in Foreach construct we need ($something in $somethingElse). Correct me if I am wrong.

Comments

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.