【问题标题】:how to prevent external script from terminating your script with break statement如何防止外部脚本使用 break 语句终止您的脚本
【发布时间】:2018-01-26 13:06:47
【问题描述】:

我正在调用一个外部 .ps1 文件,该文件在某些​​错误条件下包含 break 语句。我想以某种方式捕捉这种情况,允许任何外部打印的消息正常显示,并继续我的脚本中的后续语句。如果外部脚本有throw,则使用try/catch 可以正常工作。即使在我的文件中有trap,我也无法阻止我的脚本终止。

为了回答这个问题,假设外部 .ps1 文件的源代码(由其他人创作并在运行时引入)无法更改。

我想要的是否可能,或者脚本的作者只是不想在外部调用时表现得很好?

编辑:提供以下示例。

在 badscript.ps1 中:

if((Get-Date).DayOfWeek -ne "Yesterday"){
    Write-Warning "Sorry, you can only run this script yesterday."
    break
}

在 myscript.ps1 中:

.\badscript.ps1
Write-Host "It is today."

我想要达到的结果是看到来自 badscript.ps1 的警告,并让它继续我在 myscript.ps1 中的进一步声明。我理解为什么 break 语句会导致“It is today”。永远不会被打印,但是我想找到解决方法,因为我不是 badscript.ps1 的作者。

编辑:将标题从“powershell tr​​y/catch 不捕获中断语句”更新为“如何防止外部脚本使用中断语句终止脚本”。提到 try/catch 实际上更多的是新标题更好地反映了实际问题的一个失败的解决方案。

【问题讨论】:

  • break 不会触发错误,不会。如果break 语句仅在脚本中抛出错误的条件下被调用,您可以设置$ErrorActionPreference = 'Stop'
  • trycatch 用于例外情况,throw 也是如此。 break 与此无关。你的问题没有意义/
  • @EJP 这个问题的重点是是否有一种机制可以让我获得类似的结果来捕获异常。对我来说很有意义:如何获得外部脚本调用而不停止脚本的执行?
  • @MathiasR.Jessen 不幸的是,一些 break 语句是在非错误条件语句之后使用的,所以也许我在称它们为“错误条件”时不够精确。此外,我不希望全局强制所有其他脚本非终止错误成为终止错误,因为它可以优雅地处理一些错误。
  • 如果您包含一些您现在拥有的示例,这个问题可能会更有意义,这样我们就可以看到您正在尝试解决的问题。这个问题似乎不清楚,cmets 正试图澄清这一点。见minimal reproducible example

标签: powershell


【解决方案1】:

从我的脚本中运行一个单独的 PowerShell 进程来调用外部文件最终成为一个足以满足我需求的解决方案: powershell -File .\badscript.ps1 将执行 badscript.ps1 的内容,直到 break 语句包括任何 Write-Host 或 Write-Warning,然后让我自己的脚本继续执行。

【讨论】:

    【解决方案2】:

    我知道你来自哪里。可能最简单的方法是将脚本作为一项工作推出,然后等待结果。如果需要,您甚至可以在完成后使用 Receive-Job 回显结果。

    所以考虑到您上面的错误脚本,以及调用它的脚本文件:

    $path = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
    
    $start = Start-Job -ScriptBlock { . "$using:Path\badScript.ps1" } -Name "BadScript"
    
    $wait = Wait-Job -Name "BadScript" -Timeout 100
    Receive-Job -Name "BadScript"
    Get-Command -Name "Get-ChildItem"
    

    这将执行作业中的错误脚本,等待结果,回显结果,然后继续执行它所在的脚本。

    这可以包装在一个函数中,用于您可能需要调用的任何脚本(只是为了安全起见。

    这是输出:

    WARNING: Sorry, you can only run this script yesterday.
    
    CommandType     Name                                               Version    Source
    -----------     ----                                               -------    ------
    Cmdlet          Get-ChildItem                                      3.1.0.0    Microsoft.PowerShell.Management
    

    【讨论】:

    • 开始工作时 |与我对需要同步的东西的回答相比,Wait-Job 稍微复杂一些,我真的很感激这个完全保留在命令行开关中的问题!
    【解决方案3】:

    about_Break documentation 它说

    PowerShell 不限制标签可以恢复执行的距离。这 label 甚至可以跨脚本和函数调用传递控制 边界。

    这让我想,“我该如何欺骗这种愚蠢的语言设计选择?”。答案是创建一个小的switch 块,它将在退出时捕获break

    .\NaughtyBreak.ps1

    Write-Host "NaughtyBreak about to break"
    break
    

    .\OuterScript.ps1

    switch ('dummy') { default {.\NaughtyBreak.ps1}}
    
    Write-Host "After switch() {NaughtyBreak}"
    
    .\NaughtyBreak.ps1
    
    Write-Host "After plain NaughtyBreak"
    

    然后当我们调用 OuterScript.ps1 时,我们得到

    NaughtyBreak about to break
    After switch() {NaughtyBreak}
    NaughtyBreak about to break
    

    请注意,OuterScript.ps1 在调用嵌入在 switch 中的 NaughtyBreak.ps1 后正确恢复,但在直接调用 NaughtyBreak.ps1 时被毫不客气地杀死。

    【讨论】:

    • 如果 NaughtyBreak.ps1 有break non_existing_label,它将终止调用者脚本。 (显然,这只有在 NaughtyBreak 作者有意这样做时才会发生。)
    • @Silvar 很好。这似乎是一个安全问题。想象一下 NaughtyBreak.ps1 知道调用者脚本有一个特定的封闭标签。然后它可以中断执行到该标签,并且外部脚本无法阻止它。看起来很不对劲。
    【解决方案4】:

    将break放回它所属的循环(包括开关)中。

    foreach($i in 1) { ./badscript.ps1 } 
    'done'
    

    或者

    switch(1) {  1 { ./badscript.ps1 } }
    'done'
    
    

    【讨论】:

    • 哈哈,聪明。从其他语言的经验来看,我从未预料到 break 语句会影响不同文件/函数中的流结构:)
    • 直到你的问题我才知道。我知道你调用的脚本可以继承第一个脚本的变量。但它们都有自己的范围。
    • 顺便说一句,如果 badscript 作者很“聪明”并积极尝试使调用者脚本终止,这将不起作用:如果他们使用不存在的标签限定 break,调用者脚本将永远终止。当然,调用者脚本的作者可以检查 badscript 并确保对它的调用包含在所需的标记循环/开关中以规避这一点。
    猜你喜欢
    • 2010-11-19
    • 2012-04-30
    • 2011-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-29
    • 1970-01-01
    相关资源
    最近更新 更多