Reputation: 143
I wrote out a simple PowerShell script that backs up a directory to C:\
and then deletes any of the backup folders when its age = X days.
For some reason, when I use the Remove-Item
cmdlet I'm getting a Remove-Item: Cannot find path 'C:\Windows\system32\ [Sub-Folder name]' because it does not exist
error.
Below is the snippet:
$TargetFolder = "C:\Folder\"
$Folders = get-childitem -path $TargetFolder
foreach ($Folder in $Folders)
{
remove-item $Folder -recurse -force
}
Within the $TargetFolder = "C:\Folder\"
, there are a few sub-folders.
Examples: C:\Folder\SubfolderA
, C:\Folder\SubfolderB
, etc.
When I do a Write-Host
for $Folder
it lists SubFolderA
, SubFolderB
, etc, correctly so I'm not exactly sure why I'm getting a Cannot find path
error.
Upvotes: 2
Views: 23541
Reputation: 437363
tl;dr
To ensure that Remove-Item
correctly identifies a directory object as returned by Get-ChildItem
(an instance of type [System.IO.DirectoryInfo]
):
When passing the object as a parameter value (argument), in Windows PowerShell (no longer in PowerShell Core) you must use .FullName
for the command to work reliably:
Remove-Item -LiteralPath $Folder.FullName ... # !! Note the need for .FullName
-LiteralPath
is not strictly needed, but is the more robust choice, given that Remove-Item $Folder.FullName
implicitly binds to the -Path
parameter instead, which interprets its argument as a wildcard expression; often this will make no difference, but it can.
When using the pipeline, you can pass the objects as-is.
Get-ChildItem -Directory | Remove-Item ...
The surprising need to use .FullName
is the result of a design quirk; the resulting behavior and its implications are discussed below; a fix has been proposed in this GitHub issue.
Liturgist's helpful answer and AJK's helfpul answer contain complementary pieces of the solution (Liturgist's answer has since been amended to provide a complete solution):
To limit what Get-ChildItem
returns to directories (folders), you must use -Directory
(PSv3+).
To unambiguously identify a filesystem object when passing it to Remove-Object
as a parameter that is converted to a string, its full path must be specified.
Note: The situational filename-only stringification described below affects only Windows PowerShell; fortunately, the problem has been fixed in PowerShell Core, fortunately.
In the case at hand, given that $Folder
contains a [System.IO.DirectoryInfo]
object as returned by Get-ChildItem
, $Folder.FullName
is simplest (but constructing the path by prepending $TargetPath
works too).
In Windows PowerShell, even though $Folder
is a [System.IO.DirectoryInfo]
object that does contain the full path information, when converted to a string it situationally may only expand to its mere directory name (last path component) - it depends on how the [System.IO.DirectoryInfo]
and [System.IO.FileInfo]
instances were obtained, namely:
If Get-ChildItem
was called either without a path argument or via a path argument that is a directory, the output objects stringify to their mere file/directory name - see this answer for details.
Simple example: $d = (Get-ChildItem -Directory $HOME)[0]; "$d"
yields Contacts
, for instance, not C:\Users\jdoe\Contacts
.
Remove-Item
, PowerShell does use the full path.Important: If you do pass the target folder as a parameter and neglect to specify the full path while not in the same location as the target folder, the name may therefore interpreted as relative to the current location, and you'll either get a Cannot find path
error - as you saw - or, even worse, you may end up deleting a different folder by the same name if one happens to be present in your current location (directory).
As stated, you can avoid the full-path problem by piping the folder objects returned by Get-ChildItem
to Remove-Item
, which also enables a more elegant, single-pipeline solution (PSv3+):
$TargetFolder = "C:\Folder"
$Days = 5
Get-ChildItem -Directory $TargetFolder |
Where-Object LastWriteTime -lt (Get-Date).Date.AddDays(-$Days) |
Remove-Item -WhatIf -Force -Recurse
Remove the -WhatIf
to perform actual removal.
Upvotes: 3
Reputation: 16236
It seems that you want to do this on the basis of the directory LastWriteTime, but you did not mention -Directory on Get-ChildItem.
[cmdletbinding()]
Param()
$TargetFolder = "C:\Users\lit\Documents"
$Folders = Get-ChildItem -Path $TargetFolder -Directory
$Days = 80
foreach ($Folder in $Folders) {
if ($Folder.LastWriteTime -lt (Get-Date).AddDays(-$Days)) {
Write-Verbose "Deleting directory $($Folder.FullName)"
Remove-Item -WhatIf "$($Folder.FullName)" -Recurse -Force
}
}
Upvotes: 6
Reputation: 21
Try executing remove-item on the full path, e.g.
$TargetFolder = "C:\Folder\"
$Folders = get-childitem -path $TargetFolder
foreach ($Folder in $Folders)
{
remove-item $TargetFolder$Folder -recurse -force
}
Upvotes: 2