PowerShell脚本foreach循环仅处理首行输入后终止的故障排查
故障原因
脚本仅处理输入文件第一行就终止,是PowerCLI模块默认错误行为和现有错误处理逻辑缺失共同导致的,这也是你之前用同类foreach结构处理普通文本、调用原生PowerShell cmdlet没出过问题,这次跑VMware相关脚本就异常的核心原因:
- VMware PowerCLI模块的cmdlet默认遇到异常会抛出终止错误,一旦某轮循环内的命令执行失败,会直接跳出整个foreach循环,不会继续处理后续行;而原生PowerShell cmdlet默认抛出非终止错误,单条出错不会打断循环,两者行为存在差异。
- 现有
Try/Catch块作用范围覆盖了整个循环,但Catch块完全为空,错误发生后没有任何提示、也没有日志记录,直接静默终止,无法感知具体报错点。 - 循环内的查询逻辑没有做对象存在性校验,任意一种常见场景都会触发错误终止:
- 输入行拼接得到的
$ClusterName对应集群不存在 - 目标集群下没有名称匹配
*190*规则的虚拟机 - 查询得到的
$VM对象为空时,直接传入Get-HardDisk会触发参数绑定错误
- 输入行拼接得到的
- 额外逻辑问题:脚本启动日志写在了foreach循环内部,每处理一行就会重复记录一次启动信息,不符合日志逻辑。
修复方法
按以下规则调整代码即可:
- 配置全局错误策略,确保所有错误可被捕获,单条条目处理失败不终止整个脚本
- 增加空行过滤逻辑,跳过输入文件里的空白行,避免拼接无效集群名
- 每一步查询后增加对象存在性校验,空对象提前记录日志并跳过当前轮次
- 补全Catch块的错误日志逻辑,单条条目处理出错时记录错误信息,继续处理下一行
- 将脚本启动日志挪到循环外部,避免重复记录
修正后的核心逻辑参考:
# Create log file directory New-Item -Path "C:\scripts\logs" -ItemType "directory" -ErrorAction SilentlyContinue # Set script name $ScriptName = "Hard_Disk_Count.ps1" # Set script log file $LogFile = "C:\scripts\logs\Hard_Disk_Count.log" # Clear script log file Clear-Content -Path "C:\scripts\logs\Hard_Disk_Count.log" -ErrorAction SilentlyContinue # Import VMware modules Write-Host "Importing VMWare modules." -ForegroundColor Cyan Import-Module VMware.VumAutomation Import-Module VMware.VimAutomation.Core Import-Module VMware.VimAutomation.Storage Import-Module VMware.VimAutomation.Common Import-Module VMware.VimAutomation.License # Connect to a vCenter $VIServer = "example" Connect-VIServer -Server $VIServer # 全局错误策略配置 $ErrorActionPreference = "Stop" Try { # Prompt for confirmation that input file is updated $confirmation = Read-Host "Please confirm that the text file located at 'C:\scripts\input.txt' contains the correct information.`nEnter 'y' to proceed." if ( $confirmation -eq 'y' ) { # 读取输入文件,过滤空行 $InputFile = Get-Content "C:\scripts\input.txt" -Encoding UTF8 | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } # 启动日志挪到循环外 Write-Host "Script $ScriptName started. Log file located at $LogFile." -ForegroundColor Cyan "$(Get-Date) - Script $ScriptName started." >> $LogFile foreach( $I in $InputFile ){ try { $ClusterName = "${I}_Cluster" # 校验集群存在性 $cluster = Get-Cluster -Name $ClusterName if(-not $cluster){ "$(Get-Date) - 跳过条目 $I : 集群 $ClusterName 不存在" >> $LogFile continue } $VM = $cluster | Get-VM | Where-Object { $_.Name -like "*190*" } if(-not $VM){ "$(Get-Date) - 跳过条目 $I : 集群 $ClusterName 下无匹配*190*规则的虚拟机" >> $LogFile continue } $Disks = Get-HardDisk -VM $VM $DiskCount = $Disks.count "$(Get-Date) - 统计结果:VM $($VM.Name), 硬盘数量 $DiskCount" >> $LogFile } catch { # 记录单条错误,不中断循环 $err = $_.Exception.Message "$(Get-Date) - 处理条目 $I 失败:$err" >> $LogFile Write-Host "处理 $I 失败:$err" -ForegroundColor Red } } } } Catch { # 全局错误处理,覆盖连接vCenter失败、读输入文件失败这类全局异常 $err = $_.Exception.Message "$(Get-Date) - 脚本全局错误:$err" >> $LogFile Write-Host "脚本运行出错:$err" -ForegroundColor Red # 这里补充原有的发邮件逻辑即可 }
调整后如果运行仍有异常,可以直接打开日志文件查看对应条目的报错信息,就能定位是集群不存在、权限不足还是VM查询规则不匹配的具体问题。
内容的提问来源于stack exchange,提问作者HipHippopatomus
相关产品推荐
相关产品推荐

