I have a PowerShell script that recursively deletes all files and folders, but excludes certain folders as they should not be deleted.
It works 100%, but my problem is performance. I need this to run a lot faster.
Any ideas on how to make this faster?
Write-Host "Purging $InstallationDirectorySite - Deleting files..."
$FolderExlusions = (
"App_Data",
"Logs",
"TEMP",
"ExamineIndexes",
"DistCache",
"GitPathProviderRepository"
)
[regex] $files_regex = "Logs|ExamineIndexes|DistCache*|GitPathProviderRepository*"
if(Test-Path $InstallationDirectorySite) {
Get-ChildItem -Path $InstallationDirectorySite -Recurse -Exclude $FolderExlusions |
Where-Object {$_.FullName -notmatch $files_regex} |
Remove-Item -Recurse
}
else {
Write-Output "$InstallationDirectorySite doesn't exist"
}
You are in fact filtering the excluded folders twice.
The first time using the -Exclude parameter and the second time using a Regex -match.
However, the Exclude parameter takes a string array, not a single string with keywords separated by a comma and a newline as you get from the 'here-string'.
See Get-ChildItem
Also, the regex you use is wrong, because the asteriks * in regex is a quantifier, not a wildcard character.
I suggest you filter once using either the -Exclude parameter like this (here the asteriks is a Wildcard):
$FolderExlusions = "App_Data","Logs","TEMP","ExamineIndexes","DistCache*","GitPathProviderRepository*"
Get-ChildItem -Path $InstallationDirectorySite -Recurse -Exclude $FolderExlusions | Remove-Item -Recurse -WhatIf
Or use only the regex method in a Where-Object clause like this:
$FolderExlusions = "^(App_Data|Logs|TEMP|ExamineIndexes|DistCache.*|GitPathProviderRepository.*)"
Get-ChildItem -Path $InstallationDirectorySite -Recurse | Where-Object { $_.Name -notmatch $FolderExlusions } | Remove-Item -Recurse -WhatIf
Remove the -WhatIf if you are satisfied with the results.
Hope that helps
Related
We have a situation where the cp.exe and cp.dll are getting copied in build artifacts of all the folders and we want them only to be part of PS.Manager.Service and PS.A.Service folder/Artifacts.
From rest all the folders both the cp.exe and its cp.dll must be deleted. I tried doing that with the below script but It is not working. I am not sure what am I missing here.
Any suggestions would really help.
Thanks.
$SourceDirectory = "e:\cache\Agent03\2\s"
$EXcludeManagerFolder = "e:\cache\Agent03\2\s\PS.Manager.Service"
$EXcludeAFolder = "e:\cache\Agent03\2\s\PS.A.Service"
gci -path "$SourceDirectory" -Include "cp.exe, cp.dll" -Exclude "$EXcludeManagerFolder","$EXcludeAFolder " -Recurse -Force | Remove-Item -Recurse -Force
As zett42 already commented, Include, Exclude (and also Filter) only work on the objects Name, not the full path.
The easiest way is to create a regex string with the folders you need to exclude so you can match (or -notmatch in this case)
$SourceDirectory = "e:\cache\Agent03\2\s"
# create a regex of the files and/or folders to exclude
$exclude = 'PS.Manager.Service', 'PS.A.Service' # foldernames to exclude
# each item will be Regex Escaped and joined together with the OR symbol '|'
$notThese = ($exclude | ForEach-Object { [Regex]::Escape($_) }) -join '|'
(Get-ChildItem -Path $SourceDirectory -Include 'cp.exe', 'cp.dll' -File -Recurse).FullName |
Where-Object { $_ -notmatch $notThese } |
Remove-Item -Force
Hello I would like to know is there a way to replace multiple filenames from parent folder?
I was able to rename multiple files using command below, but I have to access each folder first to rename multiple files.
dir .\* -include ('*.mp4','*.srt') | Rename-Item -NewName { $_.Name -replace '\[','' -replace '\]','' }
I was trying to replace dir .\* to dir .\**\* to select from parent folder but didn't work.
What am I missing?
You can do that by adding the -Recurse switch to Get-ChildItem (dir is alias to Get-ChildItem).
When searching for files that contain square brackets, you need to use the -LiteralPath parameter.
Also, be aware that there is a snag if you pipe the results from that directly to Rename-Item..
When doing so, Get-ChildItem may pick up the already processed files again, wasting time of course, so to prevent that you can either do:
$rootFolder = 'X:\WhereTheFilesAre'
(Get-ChildItem -LiteralPath $rootFolder -Include '*.mp4','*.srt' -File -Recurse) |
Rename-Item -NewName { $_.Name -replace '\[|]' }
or
$rootFolder = 'X:\WhereTheFilesAre'
$files = Get-ChildItem -LiteralPath $rootFolder -Include '*.mp4','*.srt' -File -Recurse
foreach ($file in $files) {
$file | Rename-Item -NewName { $file.Name -replace '\[|]' }
}
In additional to marsze's concise answer and realizing this isn't really a performance question:
-Include is much slower than -Filter because it filters after retrieving from the file system. Whereas, -Filter has the file system do the heavy lifting. In essence -Filter is a nuanced version of moving filtering operations left in the command/pipeline to improve performance. However, -Filter doesn't take multiple values! That said, you may still be able to exploit this characteristic using a loop, something like:
'*.mp4','*.srt' |
ForEach-Object{
Get-ChildItem .\ -Filter $_ -File
} |
Rename-Item -NewName { $_.Name -replace '\[|\]' }
-File works even though you are using -Recurse and may carry a modest performance improvement. I also shortened the -replace to 1 operation which can only help further.
What you probably want is the -Recurse switch:
dir "the folder" -Recurse -Include ('*.mp4','*.srt')
Note that this will recurse all levels of subdirectories.
I just found out I could use dir . to select all files (with specific file types), including files in subdirectories.
dir . -Recurse -Include ('*.srt', '*.mp4') | Rename-Item -NewName { $_.Name -replace '\[|\]','' }
I am trying to write a script in Powershell to remove some files automatically with a certain file name.
My idea is to get all the folders in the directory, then loop through the subdirectory, and remove all items with the file name, but it doesn't seem to be working as expected.
Here is my script
$folders = Get-ChildItem -path "C:\Website-Backup" -Recurse | Where-Object {$_.PsIsContainer} |Group-Object {$_.FullName.Split('_')[0] }
$subfolders = Get-ChildItem -path $folders -Recurse | Where-Object {$_.PsIsContainer} | Group-Object {$_.FullName.Split('_')[0] }
ForEach($subfolder in $subfolders)
{
Remove-Item * -Include *100x*
}
Any idea why the script doesn't seem to be doing anything?
I think you can simplify your code if I understand correctly to:
Get-ChildItem "C:\Website-Backup" -Recurse -include "*100x*" -file | remove-item
The Group-Object command is likely what's confusing things here - Remove-Item is expecting a path - you're not referencing the subfolder variable in your loop as well, so this is the same as just running the Remove-Item command as many times as there are items in the array.
You can try this instead;
Get-ChildItem -Path "C:\Website-Backup" -Recurse | Where-Object -FilterScript { $_.name -like 'MyFile.txt' } | Remove-Item
This will pipe the returned child items into Where-Object, filter it to the specified file name, then pass that to Remove-Item as a file path.
You can also skip the Where-Object, but you lose a bit of control this way;
Get-ChildItem -Path 'C:\WebSiteBackup\*MyFile.txt' -Recurse | Remove-Item
I am attempting to delete all directories, sub-directories, and the files contained in them based on a filter that specifies the required directory/sub-directory name.
For example, if I have c:\Test\A\B.doc, c:\Test\B\A\C.doc, and c:\Test\B\A.doc and my filter specifies all directories named 'A', I would expect the remaining folders and files to be c:\Test, c:\Test\B and c:\Test\B\A.doc respectively.
I am trying to do this in PowerShell and am not familiar with it.
The following 2 examples will delete all of the files that match my specified filter, but the files that match the filter as well.
$source = "C:\Powershell_Test" #location of directory to search
$strings = #("A")
cd ($source);
Get-ChildItem -Include ($strings) -Recurse -Force | Remove-Item -Force –Recurse
and
Remove-Item -Path C:\Powershell_Test -Filter A
I would use something like this:
$source = 'C:\root\folder'
$names = #('A')
Get-ChildItem $source -Recurse -Force |
Where-Object { $_.PSIsContainer -and $names -contains $_.Name } |
Sort-Object FullName -Descending |
Remove-Item -Recurse -Force
The Where-Object clause restricts the output from Get-ChildItem to just folders whose names are present in the array $names. Sorting the remaining items by their full name in descending order ensures that child folders get deleted before their parent. That way you avoid errors from attempting to delete a folder that had already been deleted by a prior recursive delete operation.
If you have PowerShell v3 or newer you can do all filtering directly with Get-ChildItem:
Get-ChildItem $source -Directory -Include $names -Recurse -Force |
Sort-Object FullName -Descending |
Remove-Item -Recurse -Force
I don't think you can do it quite that simply. This gets the list of directories, and breaks the path into its constituent parts, and verifies whether the filter matches one of those parts. If so, it removes the whole path.
It adds a little caution to handle if it already deleted a directory because of nesting (the test-path) and the -Confirm helps ensure that if there's a bug here you have a chance to verify the behavior.
$source = "C:\Powershell_Test" #location of directory to search
$filter = "A"
Get-Childitem -Directory -Recurse $source |
Where-Object { $_.FullName.Split([IO.Path]::DirectorySeparatorChar).Contains($filter) } |
ForEach-Object { $_.FullName; if (Test-Path $_) { Remove-Item $_ -Recurse -Force -Confirm } }
I would like to scan and move folders (and sub folders or even deeper) from one folder to another using Powershell.
Currently I'm using this pipe of commands.
Get-ChildItem -recurse -path sub\WORK -filter "* OK" | Where-Object { $_.PSIsContainer } | foreach { Move-Item -path $_ -destination sub\OK }
Unfortunately it doesn't work because the found results are relative to .\sub\WORK, when trying to move them Move-Item complains that the folders are not in the current folder:
Move-Item : Cannot find path 'C:\TMP\2011-12-12 test 2 OK' because it does not exist.
I expect that $_ would contain: 'C:\TMP\sub\WORK\2011-12-12 test 2 OK' because these are objects in Powershell and no strings like in Linux.
In case you use Get-ChildItem, be very careful. The best way is to pipe the objects to Move-Item and you don't need to think about it more:
Get-ChildItem -recurse -path sub\WORK -filter "* OK" | Where-Object { $_.PSIsContainer } | Move-Item -destination sub\OK
(no need to use Foreach-Object)
The main reason why I'm answering is this one: Get-ChildItem constructs object differently, depending on the parameters. Look at examples:
PS C:\prgs\tools\Console2> gci -include * | % { "$_" } | select -fir 5
C:\prgs\tools\Console2\-verbose
C:\prgs\tools\Console2\1UpdateDataRepositoryServices.ps1
C:\prgs\tools\Console2\22-52-59.10o52l
C:\prgs\tools\Console2\2jvcelis.ps1
C:\prgs\tools\Console2\a
PS C:\prgs\tools\Console2> gci | % { "$_" } | select -fir 5
-verbose
1UpdateDataRepositoryServices.ps1
22-52-59.10o52l
2jvcelis.ps1
a
Then if you use $_ in a cycle and PowerShell needs to convert FileInfo from Get-ChildItem to string, it gives different results. That happened when you used $_ as argument for Move-Item. Quite bad.
I think there is a bug that reports this behaviour.
You are correct that objects are being piped down the pipeline instead of strings. This is good in that it is more flexible. The drawback is that if you don't explicitly tell the system which property of the object to use you are at the mercy of the system designers. See if explicitly telling the system the property that you want will help:
Get-ChildItem -recurse -path sub\WORK -filter "* OK" | Where-Object { $_.PSIsContainer } | foreach { Move-Item -path $_.Fullname -destination sub\OK }
I just learn't that the PSPath is automatically used in Copy-Item, Move-Item etc. when you don't specify the source in a pipeline, so something like:
gci .\sub\Work | move-item -Destination .\sub\OK
(simplified example)
would work and it would use the PSPath of the passed object to determine the source.
Since the Get-ChildItem returns objects like you said, you can use Get-Member to see what the object has to offer ( that is know about its properties and methods)
Get-ChileItem path | Get-Member
You could see that FullName is one of the properties that you could use.
Here is what worked for me.
Get-ChildItem -Path .\ -Recurse -filter "* OK" | %{Join-Path -Path $_.Directory -ChildPath $_.Name } | Move-Item -Destination sub\OK