PowerShell删除Exchange空公用文件夹脚本报错时,如何移除报错文件夹以终止无限循环?
PowerShell删除Exchange空公用文件夹脚本报错时,如何移除报错文件夹以终止无限循环?
我之前也遇到过一模一样的问题:用PowerShell批量删除Exchange空公用文件夹时,只要有某个文件夹删除报错,它就会在每次循环里重新被检测到,导致脚本无限循环跑下去。核心问题就是没把报错的文件夹从后续的检测列表里排除掉,多亏了@Michał Turczyn的提示,我先找到了基础解决方法,后来又逐步优化了性能、日志和通知功能,下面分享完整的思路和最终脚本:
最初的问题脚本
先看看我最开始写的有问题的版本,每次循环都会重新拉取所有符合条件的文件夹,但完全没处理报错的文件夹:
# Loop until all empty folders are deleted do { # Get all Public Folders without Mail and Subfolders $folders = Get-PublicFolder -Recurse -resultsize unlimited | where{$_.MailEnabled -eq $false -and $_.hassubfolders -eq $false} # Filter Folders without Items $emptyFolders = $folders | Where-Object -FilterScript {@($_ | Get-PublicFolderStatistics).ItemCount -eq 0} # Delete Folders $emptyFolders | select folderpath | ForEach-Object {$_.folderpath -join "\\"} | ForEach-Object {"\\" + $_} | Remove-PublicFolder -confirm:$false } until ($emptyFolders -eq $null)
这个脚本的致命问题:如果某个文件夹删除失败(比如权限不足、被其他进程占用),它会在每次循环的$folders里重新出现,导致无限循环无法终止。
核心解决思路
要打破无限循环,关键要做两件事:
- 维护一个报错文件夹列表,每次删除失败就把该文件夹的唯一标识(
EntryID)存入列表 - 后续循环检测空文件夹时,直接排除这个列表里的文件夹
- 同时优化Exchange查询逻辑,减少重复拉取数据,降低对服务器的负载
最终优化后的脚本
经过两次迭代,我最终的脚本支持多基文件夹检测、错误跟踪、日志记录、邮件通知等功能,完全解决了无限循环问题,还提升了性能:
## Delete empty public folders ## # Load Exchange cmdlet Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn; # Start logging $loglocation = "C:\WINDOWS\TEMP\pf_delete_" + (Get-Date).ToString("yyyy-MM-dd") + ".log" Start-Transcript -Path $loglocation #Set base public folders $pfpaths = @( "\path\to\folder\one" "\path\to\folder\two" "\path\to\folder\three" ) # Run for each base public folder in $pfpaths foreach ($pfpath in $pfpaths) { Write-Host "**********************" Write-Host "Process folders in path: $pfpath\." # Track folders with error $errorFolders = @() # Get public folders (only query once per base folder for better performance) $folders = Get-PublicFolder $pfpath -Recurse -ResultSize Unlimited # Loop until all empty folders are deleted do { # Track parent folders to re-check after deletion $parentFolders = @() # Filter folders: exclude mail-enabled, with subfolders, errored, and base folders $filterFolders = $folders | Where-Object { $_.MailEnabled -eq $false ` -and $_.HasSubFolders -eq $false ` -and ($errorFolders -notcontains $_.EntryID) ` -and ($pfpaths -notcontains $_.Identity) } # Filter folders with zero items $emptyFolders = $filterFolders | Where-Object -FilterScript {@($_ | Get-PublicFolderStatistics).ItemCount -eq 0} # Attempt to delete each empty folder foreach ($emptyFolder in $emptyFolders) { try { Remove-PublicFolder -Identity $emptyFolder.EntryID -Confirm:$false -ErrorAction Stop Write-Host "- Deleted folder: $($emptyFolder.ParentPath)\$($emptyFolder.Name)\." $parentFolders += $($emptyFolder.ParentPath) } catch { Write-Warning "Folder with error: $($emptyFolder.ParentPath)\$($emptyFolder.Name)\." $errorFolders += $emptyFolder.EntryID } } # Re-query parent folders to check if they became empty after deletion $folders = @() $parentFolders = $parentFolders | Select-Object -Unique $parentFolders = $parentFolders | Where-Object { ($pfpaths -notcontains $_) } foreach ($parentFolder in $parentFolders) { $folders += Get-PublicFolder $parentFolder -Recurse -ResultSize Unlimited } $folders = $folders | Select-Object -Unique } until (-not $emptyFolders) } # Stop logging and clean up log header Stop-Transcript $output = (Get-Content $loglocation -Raw) -replace '(.+\n)+(.+)?(?=output file is)' $output | Out-File $loglocation ## Send log via email ## $emailbody = "The following empty public folders were automatically deleted today:`n`n" $emailbody += $(Get-Content $loglocation -Raw) # Configure email settings $fromaddress = "public-folder@domain.com" $toaddresses = "it@domain.com" $Subject = "Deleted public folders - $(Get-Date -Format dd-MM-yyyy)" $smtpserver = "mailserver.domain.com" # Build and send email $message = New-Object System.Net.Mail.MailMessage $message.From = $fromaddress foreach ($toaddress in $toaddresses) { $message.To.Add($toaddress) } $message.IsBodyHtml = $false $message.Subject = $Subject $message.Body = $emailbody $smtp = New-Object Net.Mail.SmtpClient($smtpserver) $smtp.Send($message) # Clean up log file Remove-Item $loglocation -Force
关键优化点
- 错误文件夹跟踪:用
$errorFolders数组存储删除失败的文件夹EntryID,后续筛选时直接排除,彻底解决无限循环问题 - 性能优化:每个基文件夹只查询一次完整的文件夹列表,删除子文件夹后仅重新查询父文件夹,大幅降低Exchange服务器负载
- 完整日志与通知:自动生成日志,清理无用的PowerShell转录头后,将结果邮件发送给IT团队,方便监控
- 多基文件夹支持:一次运行可处理多个不同的公用文件夹根路径,灵活性拉满
内容来源于stack exchange
相关产品推荐
相关产品推荐

