改写PowerShell脚本避免无匹配文件时等待用户输入
解决PowerShell无匹配文件时ForEach-Object挂起的问题
问题场景
运行以下代码片段时:
Get-Childitem -Filter '*.bak' -Recurse -exclude *baseline* | Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-30)} | foreach-object {
当没有符合条件的文件时,脚本会弹出提示并挂起等待输入:
cmdlet ForEach-Object at command pipeline position 3
Supply values for the following parameters:
Process[0]:
用户尝试的代码
write-host "" write-host $DBA_BackupFileShare_NME cd $DBA_BackupFileShare_NME\SQLServerBackup # @( Get-Childitem -Filter '*.bak' -Recurse -exclude *baseline* ) | # Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-30)} | # ForEach-Object # { # write-host ("{0}" -f $_.FullName) ## [System.IO.File]::Delete($_.FullName) # } [System.IO.Directory]::EnumerateFiles('.', '*.bak',[System.IO.SearchOption]::AllDirectories <# Means Recursive#>) | Where-Object {[System.IO.Path]::GetFileName($_) -notcontains 'baseline'} ForEach-Object { Get-Item -Path $_ } | Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-30)} | ForEach-Object { write-host ("{0}" -f $_.FullName) # [System.IO.File]::Delete($_.FullName) }
问题原因与解决方案
核心原因
当管道无输出对象时,ForEach-Object如果和脚本块{分两行写,PowerShell会误认为你未提供必填的Process参数,从而触发交互式输入请求,导致脚本挂起。
可行解决方案
方案1:修正脚本块语法
把ForEach-Object和脚本块的{放在同一行,消除语法歧义:
Get-ChildItem -Filter '*.bak' -Recurse -Exclude *baseline* | Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-30)} | ForEach-Object { Write-Host ("{0}" -f $_.FullName) # [System.IO.File]::Delete($_.FullName) }
方案2:强制包装为数组
用@()将Get-ChildItem的结果包装成数组,即使无匹配文件也会生成空数组,而非空管道:
@(Get-ChildItem -Filter '*.bak' -Recurse -Exclude *baseline*) | Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-30)} | ForEach-Object { Write-Host ("{0}" -f $_.FullName) # [System.IO.File]::Delete($_.FullName) }
方案3:修正你尝试的代码
你的EnumerateFiles思路可行,但有两处错误需要调整:
-notcontains是数组操作符,判断单个文件名是否包含字符串要用-notlikeWhere-Object和后续ForEach-Object之间缺少管道符|
修正后的代码:
Write-Host "" Write-Host $DBA_BackupFileShare_NME Set-Location $DBA_BackupFileShare_NME\SQLServerBackup [System.IO.Directory]::EnumerateFiles('.', '*.bak', [System.IO.SearchOption]::AllDirectories) | Where-Object {[System.IO.Path]::GetFileName($_) -notlike '*baseline*'} | ForEach-Object { Get-Item -Path $_ } | Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-30)} | ForEach-Object { Write-Host ("{0}" -f $_.FullName) # [System.IO.File]::Delete($_.FullName) }
内容的提问来源于stack exchange,提问作者pithhelmet
相关产品推荐
相关产品推荐

