Find a directory in multiple paths and delete it

by i255d at 2012-11-21 07:16:09

This is my original command:
Get-ChildItem -Path "\gha01sdp333\profile$" -Name "PowerDesigner 15" -Directory -Recurse

This is to look in the roaming profile folder of all of our users (a couple hundred), and if they have the folder "PowerDesigner 15", I want to delete it.

This is the output from that command:

Marry\AppData\Roaming\Thinstall\PowerDesigner%Common AppData%\PowerDesigner 15
Jeff\AppData\Roaming\Thinstall\PowerDesigner%Common AppData%\PowerDesigner 15
Dan\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Sybase\PowerDesigner 15
John\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Sybase\PowerDesigner 15

Two things:
A) it produces this output very slowly,
B) I wanted to use a Foreach-object command to go through the output and delete the folder, but the ouput from Get-Childitem is:

OUTPUTS
System.IO.DirectoryInfo, System.IO.FileInfo, System.String

And ForEach-object accepts -InputObject
Accept pipeline input? true (ByValue)

It seems that these are not compatable.

Can someone help me understand a better an approch that will work for this.
Currently reading "Learn Windows PowerShell3 in a Month of Lunches" on page 139.
This is also my first post.

Thanks
by ArtB0514 at 2012-11-21 07:38:06
You can most certainly pipe the output of Get-ChildItem into ForEach-Object without a problem. I suggest that you try this to verify that it works:
$Folders = Get-ChildItem -Path "\gha01sdp333\profile$" -Name "PowerDesigner 15" -Directory -Recurse
$Folders | foreach-object {$.FullName}


I’m afraid that there’s not too much you can do about speeding up searches across the network without using remoting, because (unfortunately) Get-ChildItem doesn’t have a -computername parameter.
by i255d at 2012-11-21 08:01:07
My Goal is to delete all the "PowerDesigner 15" directories. I am not sure what you added helps me do that. Are you saying that by putting the output in a variable, it makes it an object? Also, how do I get it to delete after that?
by ArtB0514 at 2012-11-21 10:26:28
It seems like you are missing some very basic concepts. Putting something into a variable doesn’t change its nature. Everything in PowerShell is an object. Object have two major categories of components: methods (things it "knows" how to do) and properties (things it "knows"). It can get very complicated becase each of the properties is also an object. You can see the components of a child item by running this (after you’ve collected $Folders object):
$Folders[0] | Get-Member
You will discover that what you have is a System.IO.DirectoryInfo .NET object and that it has a Delete method which has two formats. The first format, Delete(), only works on empty directories. The second format, Delete(bool recursive), takes an argument to delete the directory and all the files and directories under it. {You can find details of how that works by searching for "DirectoryInfo.Delete method" under MSDN.}
So at this point, you should be able to test whether this works or not by entering
$Folders[0].Delete($true)

There might be another way to delete them and you can find that by guessing that there are more commands for processing "item". Get-Command *Item will show you that there is a Remove-Item cmdlet. Get-Help Remove-Item -Full will tell you how to use that process and gives examples.
by Klaas at 2012-11-22 03:00:08
If the output is correct, you can pipe this to remove-item. No need for variables or foreach constructions.

Get-ChildItem -Path "\gha01sdp333\profile$" -Name "PowerDesigner 15" -Directory -Recurse | remove-item -whatif

The output will now be a list of operations that would be performed. If that pleases you, remove the -whatif switch and thy will be done.
by i255d at 2012-11-23 05:51:56
Piping into Remove-Item doesn’t work, because it just adds the output to the current directory and then it can’t find the directory. I think we are getting close; I am going to be working on this some more today. I am sure we can do it as described in the post prior to the previous post, but I would like to find a way to do this with cmdlets first. Also, I am still wondering if there is a faster way than Get-Childitem, which is very slow.
by Klaas at 2012-11-23 06:10:54
I tried this with files. To remove directories you should add the -recurse parameter to Remove-Item also.
Get-ChildItem -Path "\gha01sdp333\profile$" -Name "PowerDesigner 15" -Directory -Recurse | remove-item -Recurse -whatif

I don’t understand what you mean by "it just adds the output to the current directory and then it can’t find the directory". If you use the command as I wrote, the file object found by Get- ChildItem is piped to Remove-Item, so this will be the object that is removed.
by i255d at 2012-11-23 07:47:34
Remove-Item : Cannot find path ‘C:\Windows\system32\susan\AppData\Roaming\Thinstall\PowerDesigner%Common
AppData%\PowerDesigner 15’ because it does not exist.
The "C:\Windows\system32" portion of the path is my current working directory in PowerShell. The ‘susan\AppData\Roaming\Thinstall\PowerDesigner%Common\AppData%\PowerDesigner 15’ portion of the path is the output from the Get-Childitem output. The actual path needed is the portion from this : -Path "\gha01sdp333\profile$" and this: ‘susan\AppData\Roaming\Thinstall\PowerDesigner%Common\AppData%\PowerDesigner 15’
by Klaas at 2012-11-23 08:45:12
Strange. Maybe it has something to do with the share notation. Can you use -Path ‘\gha01sdp333\c$\users’ or something that is appropriate for your OS?
I get the complete path \server\share\directory\subdirectory back and the removal is performed on the remote server without any problem.
by i255d at 2012-11-23 12:22:56
Here, I am trying it with local folders and it is having the same problem.
C:\Scripts\dani\run
C:\Scripts\dani1\run
C:\Scripts\dani2\run
C:\Scripts\dani3\run
C:\Scripts\dani4\run
Current working directory in Power shell: C:\users\dani>

Get-ChildItem -Path C:\Scripts -Name "run" -Directory -Recurse | Remove-Item -WhatIf


Remove-Item : Cannot find path ‘C:\Users\dani\dani\run’ because it does not exist.
At C:\Users\diverso\AppData\Local\Temp\Untitled4.ps1:8 char:66
+ Get-ChildItem -Path C:\Scripts -Name "run" -Directory -Recurse | Remove-Item -Wh …
+ ~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (C:\Users\diverso\divero\run:String) [Remove-Item], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.RemoveItemCommand

Get-ChildItem -Path C:\Scripts -Name "run" -Directory -Recurse
dani\run
dani1\run
dani2\run
dani3\run
dani4\run
by i255d at 2012-11-23 13:04:42
I think I have it. I just wrote it how I would like it to work and it looks like it did:

Get-ChildItem -Path C:\Scripts -Name "run" -Directory -Recurse | ForEach-Object {Remove-Item -Path C:\scripts$
-Recurse}
by DexterPOSH at 2012-11-23 15:20:54
Hi,

The following will probably fail because of the use of -name parameter, If you look at the help it tells that if -name parameter is used then it Gets only the names of the items in the locations. If you pipe the output of this command to another command, only the item names are sent. Please note that instead of the file object the names are passed that’s why it doesn’t work.

Get-ChildItem -Path "\gha01sdp333\profile$" -Name "PowerDesigner 15" -Directory -Recurse | remove-item -Recurse -whatif

Instead you could use -filter parameter which will put the objects in the pipeline and then the Foreach-object or Remove-Item any cmdlet which manipulates objects works fine.

Hope it makes sense
~Cheers~
by Klaas at 2012-11-24 04:38:27
That’s it! Great find, Dexter.
I tested with -Filter indeed, but in my responses I just copied from the first post. I didn’t know that would make the difference.

@i255d
I still think the version without the foreach is better. Use -Filter ‘Name -like "PowerDesigner*"’ (maybe this needs some tuning so you really avoid any unwanted matches) and this time you will really get the objects.
You could try Measure-Command to compare the two methods.
by DexterPOSH at 2012-11-25 04:21:29
Thank you Klaas.

Before this post I never really used -name parameter with Get-Childitem , so I guess I really learned something new here.

~Cheers~
by i255d at 2012-11-26 12:16:33
I have never had much luck with Forums. A lot of the time, it is like ease dropping on a private conversation, a conversation, where you don’t know the people you are listening too. Dexter says he learned something new, but he doesn’t elaborate on what he learned. Klass says to use –filter parameter, but he doesn’t give a reference to where he would put it, or any referenced as to where someone might find information on how to make it work. The example: -Filter ‘Name -like "PowerDesigner*"’ doesn’t work at all.
So far, the only script that has worked is the one I came up with and posted myself, and it is so slow, that I can’t believe there isn’t a better way. It also doesn’t work in v2 of PowerShell.
by ArtB0514 at 2012-11-26 12:52:18
The reason that Remove-Item is giving you the error message about not finding the file to delete is that it is using the Name property from the pipeline object and joining it to the current path. Since your files are not located on the current computer, this will fail. I’m not sure that Remove-Item can delete an object on another computer, but if it can, you will have to specify the FullName property in order to identify it. Try this to see if you get better results (requires PowerShell 3):

Get-ChildItem -Path "\gha01sdp333\profile$" -Name "PowerDesigner 15" -Directory -Recurse | foreach {remove-item $_.FullName -Recurse -whatif}

You will probably still get some error messages if there are child folders in the PowerDesigner folders.
by DonJ at 2012-11-26 14:12:22
Correct - with a simple Name (e.g., MyFile.txt) Remove-Item will fail unless that items exists in the current location. It can delete from a remote machine, provided FullName (or some other property) contains a UNC, or you’ve mapped a drive for it to work with. Good point!
by Klaas at 2012-11-27 10:19:49
I’m sorry, I didn’t use the right filter syntax, it should be Get-ChildItem -Path "\gha01sdp333\profile$" -Filter "PowerDesigner 15" -Directory -Recurse | remove-item -Recurse -whatif
As everyone has pointed out, it won’t work if Remove-Item receives only a name. As Dexter found, that happens when using -Name, but when using -Filter there is more output, including the path. And that’s what remove-item needs.
Obviously I don’t have a computer named gha01sdp333 and I don’t have Powerdesigner installed, but I created ‘test’ directory with subdirectories on a remote server and the command above does work. I’m using Powershell V3, but the help files are for V2 and describe the same functionality:
Get-Help Get-ChildItem -online There’s even an example exactly like this.
And I still say there’s no need for Foreach.
by i255d at 2012-11-27 14:12:48
Here is what actually worked. I still think there has to be a faster way, this takes hours for about 200 users. I had to remove the -Directory for it to work in V2. I also had a 15 and 16 folder for PowerDesigner that I found and needed to be removed. Should I start another post to see if we can find a faster way. The server name is an NFS share. I had to make it work in V2 because it was taking so long, I had to run it from a server so I could go home. So I don’t actually know how long it took.

Get-ChildItem -Path "\gha01sdp333\profile$" -Filter "PowerDesigne*" -Recurse -ErrorAction SilentlyContinue | Remove-Item -Recurse
by DexterPOSH at 2012-11-28 07:10:11
Sorry If I was not able to explain it elaborately.

I replied to this while I was at work.
I heard J Snover say at one of the podcasts that Get-ChildItem is very slow with Network shares in PowerShell v2, but in V3 they have made few changes to make it perform better…to improve the performance you could try updating to PowerShell v3.
~Regards~
by i255d at 2012-12-03 13:40:05
I ran a test running this command in v2 and v3

Get-ChildItem -Path "\gha01sdp333\profile$" -Recurse

It took 2 hour and 18 minutes in v2
It took 1 hour and 10 minutes in v3
by megamorf at 2012-12-05 22:08:22
[quote="i255d"]Here, I am trying it with local folders and it is having the same problem.
C:\Scripts\dani\run
C:\Scripts\dani1\run
C:\Scripts\dani2\run
C:\Scripts\dani3\run
C:\Scripts\dani4\run
Current working directory in Power shell: C:\users\dani>

Get-ChildItem -Path C:\Scripts -Name "run" -Directory -Recurse | Remove-Item -WhatIf


Remove-Item : Cannot find path ‘C:\Users\dani\dani\run’ because it does not exist.
At C:\Users\diverso\AppData\Local\Temp\Untitled4.ps1:8 char:66
+ Get-ChildItem -Path C:\Scripts -Name "run" -Directory -Recurse | Remove-Item -Wh …
+ ~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (C:\Users\diverso\divero\run:String) [Remove-Item], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.RemoveItemCommand

Get-ChildItem -Path C:\Scripts -Name "run" -Directory -Recurse
dani\run
dani1\run
dani2\run
dani3\run
dani4\run[/quote]

Here’s the problem:

[1]NEM-PC {C:\Users\nem}
>gci -path "\$env:computername\C$\Chocolatey\lib" -name "tools" -Recurse
autohotkey.1.1.5.1\tools
Console2.2.0.0.20120225\tools
expresso.3.0.4334.20120225\tools
putty.0.62\tools

[2]NEM-PC {C:\Users\nem}
>gci -path "\$env:computername\C$\Chocolatey\lib" -name "tools" -Recurse | select -f 1 | gm

TypeName: System.String


versus

[3]NEM-PC {C:\Users\nem}
>gci -path "\$env:computername\C$\Chocolatey\lib" -filter "tools" -Recurse


Directory: \NEM-PC\C$\Chocolatey\lib\autohotkey.1.1.5.1


Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 09.09.2012 22:01 tools


Directory: \NEM-PC\C$\Chocolatey\lib\Console2.2.0.0.20120225


Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 09.09.2012 21:27 tools


Directory: \NEM-PC\C$\Chocolatey\lib\expresso.3.0.4334.20120225


Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 09.09.2012 21:36 tools


Directoy: \NEM-PC\C$\Chocolatey\lib\putty.0.62


Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 09.09.2012 21:36 tools

[4]NEM-PC {C:\Users\nem}
>gci -path "\$env:computername\C$\Chocolatey\lib" -filter "tools" -Recurse | select -f 1 | gm


TypeName: System.IO.DirectoryInfo


And make sure you have the correct version of powershell for the parameters you use:

Powershell Version 2
[3]NEM-PC {C:\Users\nem}
>$PSVersionTable

Name Value
---- -----
CLRVersion 2.0.50727.5466
BuildVersion 6.1.7601.17514
PSVersion 2.0
WSManStackVersion 2.0
PSCompatibleVersions {1.0, 2.0}
SerializationVersion 1.1.0.1
PSRemotingProtocolVersion 2.1


[4]NEM-PC {C:\Users\nem}
>Get-Command Get-ChildItem | Select-Object -ExpandProperty Parameters

Key

Path
LiteralPath
Filter
Include
Exclude
Recurse
Force
Name
Verbose
Debug
ErrorAction
WarningAction
ErrorVariable
WarningVariable
OutVariable
OutBuffer
UseTransaction


Versus

Powershell Version 3
[18]NEM-PC {C:\Users\nem}
>$psversiontable

Name Value
---- -----
WSManStackVersion 3.0
PSCompatibleVersions {1.0, 2.0, 3.0}
SerializationVersion 1.1.0.1
BuildVersion 6.2.9200.16398
PSVersion 3.0
CLRVersion 4.0.30319.296
PSRemotingProtocolVersion 2.2


[19]NEM-PC {C:\Users\nem}
>Get-Command Get-ChildItem | Select-Object -ExpandProperty Parameters

Key

Path
LiteralPath
Filter
Include
Exclude
Recurse
Force
Name
Verbose
Debug
ErrorAction
WarningAction
ErrorVariable
WarningVariable
OutVariable
OutBuffer
UseTransaction
Attributes
Directory
File
Hidden
ReadOnly
System
by teezee at 2013-02-13 17:00:43
here is what I did. note that you require powershell 3.0 I believe to get the -directory switch to work. also note that the -directory parameter will reduce the process time.

function deleteallmatchingfolder
{
$todelete = Get-ChildItem -Directory -recurse -Path 'H:\posh-test - Copy'

foreach ($folder in $todelete)
{

if ($folder.Name -match "folder3")
{
write-host $folder.fullname
#$folder.Delete($true)
}
}
}
deleteallmatchingfolder


simply unquote # to delete folders.

I just thought about sharing another solution…
by i255d at 2013-03-05 08:40:25
What does the - Copy' get you?

'H:\posh-test - Copy'
by teezee at 2013-03-07 18:05:24
its the name of the folder :X quote’folder’quote