Powershell:如何访问 catch block 中的迭代对象

标签 powershell

我尽量简化情况。

我有一组文件需要按特定顺序处理,因为其中一些文件相互依赖。但是我没有任何可靠的方法来确定文件在处理之前是否已完成其依赖项。 - 我所拥有的是一个外部函数,如果我过早地尝试处理文件,它会抛出一个错误。 这就是为什么我要尝试遍历这些文件,直到所有文件都已处理。

(如果您想知道变量名,这些文件包含所谓的“扩展名”。)

我想做的是,捕获无法发布到服务器但在“捕获”区域中的文件,并用剩余的集合开始 while 循环,直到所有文件都被处理或脚本达到了 20 个循环的死循环限制。

$path = 'C:\abcd'
  
$ExtList = (Get-ChildItem $path -Filter '*.app' -Recurse).FullName

$i = 0 #stop deadloop

while (($ExtList.Count -gt 0) -and ($i -lt 20)) {
    $i++
    [array]$global:FailedExt = @()
    [array]$global:FailedExtError = @()

    $ExtList | % {
        try {
            Publish-Extension -Path $_ 
        } catch {
            $global:FailedExt += $_
            $global:FailedExtError += $Error[0]
        }
    }

    #Set ExtList to list of remaining Extensions
    $ExtList = $global:FailedExt
}

if ($global:FailedExt.Count -gt 0) { #dependencies weren't the Problem
    $ErrorMsg = "Could not publish the following Extensions:`n`n"
    for ($i = 0; $i -lt $FailedExt.Count; $i++) {
        $ErrorMsg += "Error in: " + ($global:FailedExt[$i]) + ":`n"
        $ErrorMsg += " - Error: " + ($global:FailedExtError[$i]) + "`n`n"
    }
    throw $ErrorMsg
}

我的问题: 显然,catch block 中 $_ 的值与 try block 中的值不同,因为我得到 此错误输出:

Could not publish the following Extensions:

Error in: Cannot bind parameter 'Path' to the target. Exception setting "Path": "Illegal characters in path.":
 - Error: Cannot bind parameter 'Path' to the target. Exception setting "Path": "Illegal characters in path."

Error in: Cannot bind parameter 'Path' to the target. Exception setting "Path": "Illegal characters in path.":
 - Error: Cannot bind parameter 'Path' to the target. Exception setting "Path": "Illegal characters in path."

Error in: Cannot bind parameter 'Path' to the target. Exception setting "Path": "Illegal characters in path.":
 - Error: Cannot bind parameter 'Path' to the target. Exception setting "Path": "Illegal characters in path."

然后,在第二次迭代中,脚本尝试使用错误消息作为我尝试发布的文件的路径。 - 这会导致一组“路径中的非法字符”-消息。

附注如果您知道我的代码有任何其他改进,请告诉我(我是 PS 新手)

最佳答案

catch block 中,$_ 自动变量引用current error record。 ,因此它隐藏了 $_ 变量,该变量引用由 ForEach-Object(别名 %)处理的当前项。

您可以使用 foreach控制循环变量名称的语句:

foreach( $CurrentPath in $ExtList ) {
    try {
        Publish-Extension -Path $CurrentPath 
    } catch {
        $global:FailedExt += $CurrentPath
        $global:FailedExtError += $_    # Add current error record
    }
}

请注意,我还将 $Error[0] 替换为 $_,这是在 catch block 中访问错误信息的惯用方式。


ForEach-Object 的一种(可读性较差)方法是使用自动变量 $input ,它指的是 process{} block 的当前管道对象(实际上它是一个单元素数组 as discovered by mklement0 )。这在这里有效,因为传递给 ForEach-Object 的脚本 block 默认在 process{} block 中运行(除非您使用参数 -begin-end).

$ExtList | % {
    try {
        Publish-Extension -Path $_ 
    } catch {
        $global:FailedExt += $Input    # Add current ForEach-Object item
        $global:FailedExtError += $_   # Add current error record
    }
}

如果没有注释,代码就不是不言自明的,因为很少使用 $input 变量。如果您真的必须坚持使用 ForEach-Object,我建议将 $_ 分配给另一个变量,如 Joel Coehoorn's helpful answer 所解释的那样.


只是出于教育的目的,我试图找到另一种使用 Get-Variable 的方法使用参数 -Scope,它允许您从 parent scope 中获取同名变量.由于 catch 不会创建新的子作用域,我们必须使用脚本 block (& {}) 人为地创建子作用域:

$ExtList | % {
    & {         # A script block starts a new child scope
        try {
            Publish-Extension -Path $_ 
        } catch {
            # Get the $_ variable from the parent scope (the loop)
            $global:FailedExt += Get-Variable _ -Scope 1 -ValueOnly
            $global:FailedExtError += $_   # Add current error record
        }
    }           
}

虽然这可行,但我不建议使用此技术。与第一个解决方案相比,代码看起来几乎是困惑的,很难理解。

关于Powershell:如何访问 catch block 中的迭代对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71116115/

相关文章:

windows - 从 .bat 脚本运行 Powershell 命令不起作用,但是当我直接在命令行中键入它时它起作用

powershell - Get-ADUser 错误 : Invalid enumeration context

powershell - 重命名文件以删除 Powershell 中的句点

powershell - 用于在管道中跨数据集循环的 Azure PowerShell 代码

powershell - 在AD中创建用户时如何正确指定名称和属性

windows - 将变量与包含开关一起使用时 Get-ChildItem 失败

powershell - “kubectl修补程序”在Linux Bash上有效,但在Windows Powershell ISE中不可用

powershell - 检索 Azure Active Directory 中 Azure 应用程序的创建日期

powershell - 删除早于 xx 天的文件

.net - 使用 powershell 在 hyper-v(给定的 VMname)上获取 VM 的主机名