I've been working with several scripts found on here and other sources trying to work up a PowerShell script to delete thousands of empty folders that end up on our system from daily forms processing. There's several simple and effective ways of doing it, but I need a way to verify and select the folders so I don't delete the handful of folders that shouldn't be deleted even when empty.
I'm using a parameter statement for $folder so I can either input it on the command line or it will prompt for it. Using this command I get the gridview and if I simply echo the output it's fine. However, I can't seem to figure out how to pass the output to either Remove-Item or Del -Recurse. Using Del gives me:
del : Cannot find drive. A drive with the name '#{FullName=D' does not exist."
which seems that it's either not getting the output vs echo or it's looking for an array?
foreach ($folder in (dir $target -Directory -Recurse |
where {-not $_.GetFiles("*", "AllDirectories")} |
Select Fullname |
Out-GridView -PassThru -Title "Select Folders For Deletion and Click OK"
)) {
del $folder -Recurse -WhatIf
}
I suspect this will accomplish what you want. There's no need for a ForEach loop here where you're already working with a pipeline.
Get-ChildItem -LiteralPath $Target -Directory -Recurse -Force |
Where-Object { !$_.GetFiles('*','AllDirectories') } |
Select-Object -Property 'FullName' |
Out-GridView -PassThru -Title 'Select folders for deletion and click OK' |
ForEach-Object { Remove-Item -LiteralPath $_.FullName -WhatIf }
Related
so trying to find a way to combine a couple of things the Stack Overflow crowd has helped me do in the past. So I know how to find folders with a specific name and move them where I want them to go:
$source_regex = [regex]::escape($sourceDir)
(gci $sourceDir -recurse | where {-not ($_.psiscontainer)} | select -expand fullname) -match "\\$search\\" |
foreach {
$file_dest = ($_ | split-path -parent) -replace $source_regex,$targetDir
if (-not (test-path $file_dest)){mkdir $file_dest}
move-item $_ -Destination $file_dest -force -verbose
}
And I also know how to find and delete files of a specific file extension within a preset directory:
Get-ChildItem $source -Include $searchfile -Recurse -Force | foreach{ "Removing file $($_.FullName)"; Remove-Item -force -recurse $_}
What I'm trying to do now is combine the two. Basically, I'm looking for a way to tell Powershell:
"Look for all folders named 'Draft Materials.' When you find a folder with that name, get its full path ($source), then run a command to delete files of a given file extension ($searchfile) from that folder."
What I'm trying to do is create a script I can use to clean up an archive drive when and if space starts to get tight. The idea is that as I develop things, a lot of times I go through a ton of incremental non-final drafts (hence folder name "Draft Materials"), and I want to get rid of the exported products (the PDFs, the BMPs, the AVIs, the MOVs, atc.) and just leave the master files that created them (the INDDs, the PRPROJs, the AEPs, etc.) so I can reconstruct them down the line if I ever need to. I can tell the script what drive and folder to search (and I'd assign that to a variable since the backup location may change and I'd like to just change it once), but I need help with the rest.
I'm stuck because I'm not quite sure how to combine the two pieces of code that I have to get Powershell to do this.
If what you want is to
"Look for all folders named 'Draft Materials.' When you find a folder with that name, get its full path ($source), then run a command to delete files of a given file extension ($searchfile) from that folder."
then you could do something like:
$rootPath = 'X:\Path\To\Start\Searching\From' # the starting point for the search
$searchFolder = 'Draft Materials' # the folder name to search for
$deleteThese = '*.PDF', '*.BMP', '*.AVI', '*.MOV' # an array of file patterns to delete
# get a list of all folders called 'Draft Materials'
Get-ChildItem -Path $rootPath -Directory -Filter $searchFolder -Recurse | ForEach-Object {
# inside each of these folders, get the files you want to delete and remove them
Get-ChildItem -Path $_.FullName -File -Recurse -Include $deleteThese |
Remove-Item -WhatIf
}
Or use Get-ChildItem only once, having it search for files. Then test if their fullnames contain the folder called 'Draft Materials'
$rootPath = 'X:\Path\To\Start\Searching\From'
$searchFolder = 'Draft Materials'
$deleteThese = '*.PDF', '*.BMP', '*.AVI', '*.MOV'
# get a list of all files with extensions from the $deleteThese array
Get-ChildItem -Path $rootPath -File -Recurse -Include $deleteThese |
# if in their full path names the folder 'Draft Materials' is present, delete them
Where-Object { $_.FullName -match "\\$searchFolder\\" } |
Remove-Item -WhatIf
In both cases I have added safety switch -WhatIf so when you run this, nothing gets deleted and in the console is written what would happen.
If that info shows the correct files are being removed, take off (or comment out) -Whatif and run the code again.
I have a powershell script that iterates files from a certain network directory. In this directory, there are aroud 190 sub directories, each containing several files.
I use this to perform the job:
get-childitem -Path $pathFilesOrig -Recurse -Force -Include *.pdf, *.csv | ForEach {
write-host "INFO:File name: $_"
}
However, it does not loop in certain directories, whereas there are valid files in it and access rights are fine (I can access those files through windows explorer manually).
I have tried the following to check if I can at least iterate every directory:
get-childitem -Path $pathFilesOrig -Recurse -Force | ?{ $_.PSIsContainer } | ForEach {
write-host "INFO:Dir name: $_"
}
When I do it, the script displays every directory.
Now I do it to list files in each directory:
get-childitem -Path $pathFilesOrig -Recurse -Force | ?{ $_.PSIsContainer } | ForEach {
write-host "INFO:Dir name: $_"
$tmpDirName = $pathFilesOrig + $_.Name
get-childitem -Path $tmpDirName | ForEach {
Write-Host "INFO:File name: $_"
And in this case, again, some directories are missing.
There is not even the result of the second line write-host "INFO:Dir name: $_" which was working fine previously.
I don't understand, and I am certainly missing something...
Thanks for your help.
I suspect your problem has to do with the -Include parameter which filters items after the provider has collected all of the objects versus -Filter which handles it at enumeration, i.e., when it's getting the objects first. You can further improve performance by only enumerating files using the -File parameter.
#requires -Version 3
Get-ChildItem -Path $pathFilesOrig -Filter *.pdf, *.csv -File -Force -Recurse
If this command fails to resolve your problem, I suspect you actually do have a privileges problem since -Force will get hidden directories, but does not supersede access.
I am trying to search through folders on a share drive for subfolders with "*OutPuts" in the name. These folders should have folders in them but should not have files:
select fullname,#{N='SubDirectories';E={[boolean]($_ |
get-childitem -directory).count}},#{N='SubFiles';E={[boolean]($_ |
get-childitem -file).count}}
When these folders are found I want to move any folders from the output folder to the Project folder (Parent of its Parent). Example:
Starting Condition: C:\AllProjects\Projectfolder\outputs\SubProjectFolder
Wanted Outcome: C:\AllProjects\Projectfolder\SubProjectFolder
Here's the full Code:
code somewhat reformatted for readability ...
$folders=Get-ChildItem C:\AllProjects -recurse -Directory |
where {$_.name -like "*Outputs"} |
select fullname,
#{N='SubDirectories';E={[boolean]($_ |
get-childitem -directory).count}},
#{N='SubFiles';E={[boolean]($_ |
get-childitem -file).count}}
$folders
$folders |
where {$_.subdirectories -eq $true -and $_.subFile -eq $False} |
foreach {
get-childitem |
Move-Item $_ -destination $_.parent.parent}
When ever I run the code the following message comes up:
cmdlet Move-Item at command pipeline position 1
Supply values for the following parameters:
Path[0]:
I'm pretty new to PowerShell so if someone could help break this down for me and tell me what I'm supposed to put here that would be greatly appreciated. Thank you for your time.
UPDATE
*I've tried it with
-Path $_, -Path $_.PsPath, -Path $_.FullName.
I've even tried Get-Item. With the -Path though I now get a message that's a little different:
cmdlet ForEach-Object at command pipeline position 2 Supply values for the following parameters: Process[0]:
How can I delete all files in a directory that contain a string using powershell?
I've tried something like
$list = get-childitem *.milk | select-string -pattern "fRating=2" | Format-Table Path
$list | foreach { rm $_.Path }
And that worked for some files but did not remove everything. I've tried other various things but nothing is working.
I can easily get the list of file names and can create an array with the path's only using
$lista = #(); foreach ($f in $list) { $lista += $f.Path; }
but can't seem to get any command (del, rm, or Remove-Item) to do anything. Just returns immediately without deleting the files or giving errors.
Thanks
First we can simplify your code as:
Get-ChildItem "*.milk" | Select-String -Pattern "fRating=2" | Select-Object -ExcludeProperty path | Remove-Item -Force -Confirm
The lack of action and errors might be addressable by one of two things. The Force parameter which:
Allows the cmdlet to remove items that cannot otherwise be changed,
such as hidden or read-only files or read-only aliases or variables.
I would aslo suggest that you run this script as administrator. Depending where these files are located you might not have permissions. If this is not the case or does not work please include the error you are getting.
Im going to guess the error is:
remove-item : Cannot remove item C:\temp\somefile.txt: The process cannot access the file 'C:\temp\somefile.txt'
because it is being used by another process.
Update
In testing, I was also getting a similar error. Upon research it looks like the Select-String cmd-let was holding onto the file preventing its deletion. Assumption based on i have never seen Get-ChildItem do this before. The solution in that case would be encase the first part of this in parentheses as a sub expression so it would process all the files before going through the pipe.
(Get-ChildItem | Select-String -Pattern "tes" | Select-Object -ExpandProperty path) | Remove-Item -Force -Confirm
Remove -Confirm if deemed required. It exists as a precaution so that you don't open up a new powershell in c:\windows\system32 and copy paste a remove-item cmdlet in there.
Another Update
[ and ] are wildcard searches in powershell in order to escape those in some cmdlets you use -Literalpath. Also Select-String can return multiple hits in files so we should use -Unique
(Get-ChildItem *.milk | Select-String -Pattern "fRating=2" | Select-Object -ExpandProperty path -Unique) | ForEach-Object{Remove-Item -Force -LiteralPath $_}
Why do you use select-string -pattern "fRating=2"? You would like to select all files with this name?
I think the Format-Table Path don't work. The command Get-ChildItem don't have a property called "Path".
Work this snipped for you?
$list = get-childitem *.milk | Where-Object -FilterScript {$_.Name -match "fRating=2"}
$list | foreach { rm $_.FullName }
The following code gets all files of type *.milk and puts them in $listA, then uses that list to get all the files that contain the string fRating=[01] and stores them in $listB. The files in $listB are deleted and then the number of files deleted versus the number of files that contained the match is displayed(they should be equal).
sv -name listA -value (Get-ChildItem *.milk); sv -name listB -value ($listA | Select-String -Pattern "fRating=[01]"); (($listB | Select-Object -ExpandProperty path) | ForEach-Object {Remove-Item -Force -LiteralPath $_}); (sv -name FCount -value ((Get-ChildItem *.milk).Count)); Write-Host -NoNewline Files Deleted ($listA.Count - $FCount)/($listB.Count)`n;
No need to complicate things:
1. $sourcePath = "\\path\to\the\file\"
2. Remove-Item "$sourcePath*whatever*"
I tried the answer, unfortunately, errors seems to always come up, however, I managed to create a solution to get this done:
Without using Get-ChilItem; You can use select-string directly to search for files matching a certain string, yes, this will return the filename:count:content ... etc, but, internally these have names that you can chose or omit, the one you need is the "filename" to do this pipe this into "select-object" choosing the "FileName" from the output.
So, to select all *.MSG files that has the pattern of "Subject: Webservices restarted", you can do the following:
Select-String -Path .*.MSG -Pattern 'Subject: WebServices Restarted'
-List | select-object Filename
Also, to remove these files on the fly, you could pip into a ForEach statement with the RM command as follows:
Select-String -Path .*.MSG -Pattern 'Subject: WebServices Restarted'
-List | select-object Filename | foreach { rm $_.FileName }
I tried this myself, works 100%.
I hope this helps
I am trying delete all files within a folder but there is 1 folder called pictures which I would like to keep but don't know how to do that. I am using the following script , it deletes everything in a folder
if ($message -eq 'y')
{
get-childitem "C:\test" -recurse | % {
remove-item $_.FullName -recurse
}
}
One solution is to use something like:
Get-ChildItem -Path "c:\test" -Recurse | Where-Object { $_.FullName -cnotmatch "\\Pictures($|\\)" -and (Get-ChildItem $_.FullName -Include "Pictures" -Recurse).Length -eq 0 } | Remove-Item -Recurse -ErrorAction SilentlyContinue;
I suspect there must be a way more elegant way to do this. Here's what this does: it enumerates all files in the C:\test folder recursively (Get-ChildItem), then it removes all items from the result list using Where-Object where the path contains the directory to be excluded (specified using regex syntax) or when the item in question has child items that contains the file or directory to be excluded. The resulting list is fed to Remove-Item for removal. The -ErrorAction SilentlyContinue switch is applied to prevent errors being logged with recursive removal.
Get-ChildItem $PSScriptRoot -Force| Where-Object {$_.Name -ne "Pictures"} | Remove-Item -Recurse
I just tried this, and it worked for me. If you want to change what is deleted just change the "Pictures". This uses $PSScriptRoot for the path, which is the execution path of the Powershell script. You can rename that to be the path of where you want to delete.