You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 17:54:56