Reputation: 101
I want to purge log files from a build server, keeping the last few days. I have no trouble with the log files my process creates. This works fine:
$logFolders = Get-ChildItem $buildBase -Recurse -include logs -Attributes D
$logFolders | ForEach-Object { Get-ChildItem $_.fullname -filter *.log | where >{$_.lastwritetime -lt (get-date).adddays(-$purgeAfter) -and -not $_.psiscontainer} |% {remove-item $_.fullname -force} };
However, the logs produced by the build tool look like this:
lrFR_WebHelp - Friday, February 28, 2014 [9.00 PM].mclog
Get-ChildItem $frReports\Reports -filter *.mclog | where {$_.lastwritetime -lt (get-date).adddays(-$purgeAfter) -and -not $_.psiscontainer}| % {write-host ("'" + $frReports + '\Reports' + '\' + $_ + "'") }
Outputs:
'D:\scratch\Reports\lrFR_WebHelp - Friday, February 28, 2014 [9.00 PM].mclog'
But attempting to delete:
Get-ChildItem $frReports\Reports -filter *.mclog | where {$_.lastwritetime -lt (get-date).adddays(-$purgeAfter) -and -not $_.psiscontainer}| % {remove-item ("'" + $frReports + '\Reports' + '\' + $_ + "'") }
Outputs:
remove-item : Cannot find drive. A drive with the name ''D' does not exist.
At line:1 char:145
+ ... container}| % {remove-item ("'" + $frReports + '\Reports' + '\' + $_ + "'") }
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: ('D:String) [Remove-Item], DriveNotFoundException
+ FullyQualifiedErrorId : DriveNotFound,Microsoft.PowerShell.Commands.RemoveItemCommand
Changing to unix slashes ('/') or escaped slashes ('\\') doesn't change the result
This:
$filesToDelete = Get-ChildItem $frReports\Reports -filter *.mclog | where {$_.lastwritetime -lt (get-date).adddays(-$purgeAfter) -and -not $_.psiscontainer}
Push-Location -path D:\scratch\Reports
$filesToDelete | ForEach-Object {
$fname = ("'" + $_ + "'");
write-host $fname;
remove-item $fname -Force;
remove-item $_;
}
Outputs this:
'lrFR_WebHelp - Friday, February 28, 2014 [9.00 PM].mclog'
'lrFR_WebHelp - Friday, March 14, 2014 [10.24 PM].mclog'
But doesn't delete the files.
$fso = New-Object -ComObject Scripting.FileSystemObject
$buildBase = "D:\scratch";
$logFolders = Get-ChildItem $buildBase -Recurse -include Reports -Attributes D
$logFolders | ForEach-Object { Get-ChildItem $fso.GetFile($_.FullName).ShortPath -filter *.MCL | where {$_.lastwritetime -lt (get-date).adddays(-$purgeAfter) -and -not $_.psiscontainer}} #|% {write-host $_.fullname} };
Outputs:
Exception calling "GetFile" with "1" argument(s): "Exception from HRESULT: 0x800A0035 (CTL_E_FILENOTFOUND)"
At line:1 char:32
+ $logFolders | ForEach-Object { Get-ChildItem $fso.GetFile($_.FullName).ShortPath ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ComMethodTargetInvocation
Didn't have any luck getting more information about $fso.GetFile in PowerShell. Google failed me this time.
Can anyone help? I'd prefer a pure PowerShell solution, but will be grateful for anything that works.
Upvotes: 6
Views: 4934
Reputation: 200523
Your problem is caused by the square brackets in the filename. When you pass a string with a path to the (implicit) -Path
parameter of Remove-Item
, the square brackets are interpreted as wildcard characters. A substring [9.00 PM]
would thus match a single character that is either M
, P
, 9
, 0
, a dot, or a space. You could use -LiteralPath
instead of -Path
to avoid this behavior by changing this:
... | % { Remove-Item $_.FullName -Force }
into this:
... | % { Remove-Item -LiteralPath $_.FullName -Force }
However, Remove-Item
accepts pipeline input, so you could drop the loop entirely and read the items directly from the pipe:
... | Remove-Item -Force
That way Remove-Item
gets the whole FileInfo
object from Get-ChildItem
and automatically does The Right Thing™.
As a side note: you could further streamline your code by removing the loops, since not only Remove-Item
reads from the pipeline, but Get-ChildItem
as well. Something like this should work just fine:
$limit = (Get-Date).AddDays(-$purgeAfter)
Get-ChildItem $buildBase -Recurse -Include logs -Attributes D |
Get-ChildItem -Filter *.mclog |
Where-Object { $_.LastWriteTime -lt $limit -and -not $_.PSIsContainer } |
Remove-Item -Force
Upvotes: 6