PowerShell并行脚本报‘无法索引空数组’求助(PS7.3.3)
PowerShell并行执行二维数组时出现空数组索引错误的解决建议
问题背景
使用PowerShell 7.3.3编写脚本,非并行执行时正常,但添加ForEach-Object -Parallel参数后,出现核心错误Cannot index into a null array(无法索引空数组)。脚本分为两部分:
- 非并行部分:遍历匹配的tif文件,填充二维数组
$fileArr - 并行部分:通过
ForEach-Object -Parallel处理数组内容,执行图片转换操作
尝试将$using::fileArr改为$using:fileArr后,问题依然存在,错误信息如下:
无法索引空数组。
所在位置 行:2 字符:5
if ($using:fileArr[$_,3] -eq $null -and $using:fileArr[$_,4] -ne $null) {~~~~~~~~~~~~~~~~~~~~
- CategoryInfo : InvalidOperation: (:) [], RuntimeException
- FullyQualifiedErrorId : NullArray
原脚本代码
非并行部分
$fileList=(get-childitem -filter *.tif -recurse | Where-Object {$_.name -match '^[0-9]{3} (B|F) [A-Z]{2} ..K..b [0-9]{2}.*'}).Name $fileNumbers=($fileList.Substring(0,3) | Sort-Object | Get-Unique) $fileArr=New-Object 'Object[,]' $fileNumbers.length,5 for ($i=0; $i -lt $fileNumbers.length; $i++) { $fileArr[$i,0]=$fileNumbers[$i] $iStr="{0:d3}" -f ($i+1) $backFullName=@() $backName=@() $frontFullName=@() $backFullName+=(Get-ChildItem -filter "$iStr B *.tif" -recurse | Sort-Object).FullName $frontFullName+=(Get-ChildItem -filter "$iStr F CC*.tif" -recurse | Sort-Object).FullName $backName+=(Get-ChildItem -filter "$iStr B*.tif" -recurse | Sort-Object).Name $frontName+=(Get-ChildItem -filter "$iStr F CC*.tif" -recurse | Sort-Object).Name if ($backName[0] -eq $null) { $backLabel=$null } else { $backLabel=("BRP "+$backName[0].Substring(16,2)+" "+$istr) } if ($frontName[0] -eq $null) { $frontLabel=$null } else { $frontLabel=("BRP "+$frontName[0].Substring(16,2)+" "+$istr) } $fileArr[$i,1]=$backFullName[0] $fileArr[$i,2]=$frontFullName[0] $fileArr[$i,3]=$backLabel $fileArr[$i,4]=$frontLabel }
并行部分
0..($fileNumbers.length-1) | ForEach-Object -Parallel { if ($using::fileArr[$_,3] -eq $null -and $using::fileArr[$_,4] -ne $null) { $label=$using::fileArr[$_,4] } elseif ($using::fileArr[$_,3] -ne $null -and $using::fileArr[$_,4] -eq $null) { $label=$using::fileArr[$_,3] } else { $label=$using::fileArr[$_,3] } $locationA=Get-Random Convert +append $using::fileArr[$_,2] $using::fileArr[$_,1] x:\$locationA.jpg $height=[string](([int](identify -format "%h" x:\$locationA.jpg)+400)) $width=(identify -format "%w" x:\$locationA.jpg) $halfWidth=([int]$width*.4) $dimensions=$width+"x"+$height $locationB=Get-Random convert x:\$locationA.jpg -gravity north -extent $dimensions x:\$locationB.jpg $str="text $halfWidth,50 '$label'" convert x:\$locationB.jpg -pointsize 300 -gravity southwest -draw "$str" x:\$label.jpg Remove-Item x:\$locationA.jpg Remove-Item x:\$locationB.jpg } -ThrottleLimit 12
解决建议
1. 替换二维数组为一维自定义对象数组
PowerShell的并行运行空间对二维数组([object[,]])的序列化支持不佳,跨运行空间传递后会丢失结构,导致$using:fileArr变为null。改用一维数组存储自定义对象,能正常完成序列化传递:
修改非并行部分的数组构建逻辑:
$fileList=(get-childitem -filter *.tif -recurse | Where-Object {$_.name -match '^[0-9]{3} (B|F) [A-Z]{2} ..K..b [0-9]{2}.*'}).Name $fileNumbers=($fileList.Substring(0,3) | Sort-Object | Get-Unique) # 改用一维数组存储自定义对象 $fileArr = foreach ($num in $fileNumbers) { $i = [array]::IndexOf($fileNumbers, $num) $iStr="{0:d3}" -f ($i+1) $backFullName=(Get-ChildItem -filter "$iStr B *.tif" -recurse | Sort-Object).FullName $frontFullName=(Get-ChildItem -filter "$iStr F CC*.tif" -recurse | Sort-Object).FullName $backName=(Get-ChildItem -filter "$iStr B*.tif" -recurse | Sort-Object).Name $frontName=(Get-ChildItem -filter "$iStr F CC*.tif" -recurse | Sort-Object).Name $backLabel = $backName[0] ? ("BRP " + $backName[0].Substring(16,2) + " " + $iStr) : $null $frontLabel = $frontName[0] ? ("BRP " + $frontName[0].Substring(16,2) + " " + $iStr) : $null # 输出自定义对象 [PSCustomObject]@{ Number = $num BackFullName = $backFullName[0] FrontFullName = $frontFullName[0] BackLabel = $backLabel FrontLabel = $frontLabel } }
修改并行部分,直接遍历自定义对象数组:
$fileArr | ForEach-Object -Parallel { $item = $_ # 简化标签判断逻辑 $label = if (-not $item.BackLabel -and $item.FrontLabel) { $item.FrontLabel } else { $item.BackLabel } # 用GUID生成唯一临时文件名,避免并行时命名冲突 $locationA = [GUID]::NewGuid().ToString() $locationB = [GUID]::NewGuid().ToString() # 提前检查文件路径是否为空,避免Convert报错 if ($item.BackFullName -or $item.FrontFullName) { $convertArgs = @('+append') if ($item.FrontFullName) { $convertArgs += $item.FrontFullName } if ($item.BackFullName) { $convertArgs += $item.BackFullName } $convertArgs += "x:\$locationA.jpg" Convert @convertArgs $height=[string](([int](identify -format "%h" "x:\$locationA.jpg")+400)) $width=(identify -format "%w" "x:\$locationA.jpg") $halfWidth=([int]$width*.4) $dimensions="$width`x$height" convert "x:\$locationA.jpg" -gravity north -extent $dimensions "x:\$locationB.jpg" $str="text $halfWidth,50 '$label'" convert "x:\$locationB.jpg" -pointsize 300 -gravity southwest -draw "$str" "x:\$label.jpg" Remove-Item "x:\$locationA.jpg" -ErrorAction SilentlyContinue Remove-Item "x:\$locationB.jpg" -ErrorAction SilentlyContinue } } -ThrottleLimit 12
2. 解决临时文件命名冲突
原脚本用Get-Random生成临时文件名,并行执行时大概率出现重复,导致文件覆盖或报错。改用[GUID]::NewGuid().ToString()生成唯一标识符作为文件名,彻底避免冲突。
3. 增加空值前置检查
在执行Convert命令前,先检查BackFullName和FrontFullName是否至少有一个有效,避免因空路径导致的额外错误。
内容的提问来源于stack exchange,提问作者Brian
相关产品推荐
相关产品推荐

