带有 Git 命令错误处理的 Powershell - 在来自外部程序的非零退出代码时自动中止

Posted

技术标签:

【中文标题】带有 Git 命令错误处理的 Powershell - 在来自外部程序的非零退出代码时自动中止【英文标题】:Powershell with Git Command Error Handling - automatically abort on non-zero exit code from external program 【发布时间】:2018-02-19 11:16:33 【问题描述】:

我使用 Git 来部署我的 Web 应用程序。所以在 Teamcity 中,我准备了我的应用程序(编译、缩小 JS 和 html、删除未使用的文件等),然后我有一个 Powershell 构建步骤:

$ErrorActionPreference = "Stop"
git init
git remote add origin '%env.gitFolder%'
git fetch
git reset --mixed origin/master
git add .
git commit -m '%build.number%'
git push origin master

但是如果抛出异常,脚本会继续(即使我设置了$ErrorActionPreference = "Stop")并且构建成功。

我希望脚本在出现错误时停止,并且构建失败。

我尝试将Format stderr output as: error 放在构建步骤和Fail build if: an error message is logged by build runner 上,所以构建失败,但脚本继续,因此它创建了一个愚蠢的提交。

我尝试在我的脚本中放置一个 try-catch,但它没有进入 catch...

有没有人有想法停止脚本并因错误导致构建失败?

对不起我的英语,我是法国人... ^^

【问题讨论】:

TeamCity 对 Powershell 错误的处理不是很直观。我之前给过this piece of advice - 但你说 Try/Catch 没有帮助。 【参考方案1】:

$ErrorActionPreference 变量适用于调用外部实用程序([控制台]应用程序),例如git,最多至少 PowerShell 7.2.x

RFC #277 正在讨论一个潜在的未来改进,用于 将对外部程序的调用正确集成到 PowerShell 的错误处理中。

只有两种方法可以确定外部实用程序的成功与否

通过检查自动$LASTEXITCODE变量,PowerShell将其设置为外部实用程序**报告的**退出代码。 按照惯例,0 的值表示成功,而任何 非零 值表示失败。 (请注意,某些实用程序,例如 robocopy.exe,也使用某些非零退出代码来传达非错误条件。)

如果您对报告的特定退出代码不感兴趣,可以检查布尔自动变量$?,它反映了$True 用于退出代码 0$False 用于任何非零退出代码。

警告:从 PowerShell (Core) 7.1 开始,有两个 错误;这两个都通过PSNotApplyErrorActionToStderr experimental feature (!) 在 7.2 的 preview 版本中暂时修复(所有实验性功能在新安装的预览版本中默认打开) -让我们希望这些修复能够进入 7.2:

$LASTEXITCODE0 时,$? 可能会错误地反映 $false 并且使用标准错误重定向(2>*>该命令发出 stderr 输出(其本身并不一定表示失败) - 请参阅 GitHub issue #10512。

因此,养成只查询$LASTEXITCODE 而不是$? 来推断失败与成功的好习惯。

如果 $ErrorActionPreference 设置为 'Stop' 并且您碰巧将 2>*> 与外部程序一起使用并且该程序产生实际的标准错误输出,$ErrorActionPreference 出乎意料地确实适用并导致脚本终止错误(无论外部程序是否通过非零退出代码发出实际故障信号) - 请参阅GitHub issue #4002

对失败采取行动需要显式操作,通常通过使用Throw 关键字来生成脚本终止错误。


显然,在每次外部实用程序调用之后检查 $LASTEXITCODE / $? 很麻烦,所以这里有一个 包装函数 可以简化这个过程:

注意:虽然此功能有效,但它不会尝试补偿broken-up-to-at-least-v7.2.x passing of arguments with embedded double quotes to external programs。要获得此补偿,您可以使用 Native module 中的 iee 函数,可通过 Install-Module Native 从 PowerShell 库安装。

function Invoke-Utility 
<#
.SYNOPSIS
Invokes an external utility, ensuring successful execution.

.DESCRIPTION
Invokes an external utility (program) and, if the utility indicates failure by 
way of a nonzero exit code, throws a script-terminating error.

* Pass the command the way you would execute the command directly.
* Do NOT use & as the first argument if the executable name is not a literal.

.EXAMPLE
Invoke-Utility git push

Executes `git push` and throws a script-terminating error if the exit code
is nonzero.
#>
  $exe, $argsForExe = $Args
  # Workaround: Prevents 2> redirections applied to calls to this function
  #             from accidentally triggering a terminating error.
  #             See bug report at https://github.com/PowerShell/PowerShell/issues/4002
  $ErrorActionPreference = 'Continue'
  try  & $exe $argsForExe  catch  Throw  # catch is triggered ONLY if $exe can't be found, never for errors reported by $exe itself
  if ($LASTEXITCODE)  Throw "$exe indicated failure (exit code $LASTEXITCODE; full command: $Args)." 

现在您只需在所有 git 调用前添加 Invoke-Utility ,如果其中任何一个报告非零退出代码,则脚本将中止。

如果这太冗长,请为您的函数定义一个别名:Set-Alias iu Invoke-Utility,在这种情况下,您只需在前面加上 iu 

iu git init
iu git remote add origin '%env.gitFolder%'
iu git fetch
# ...

【讨论】:

对于 powershell 7.0.3 中的某些本机程序,$LASTEXITCODE 报告非零同时$? 报告 True。 i.imgur.com/4P9au0c.png 在此屏幕截图中,packer 是 packer。 刚才我意识到发生了什么——packer 和 go 都安装了scoop,它在可执行文件周围创建了一个 shim。垫片是.ps1,它会影响结果。案件结案。【参考方案2】:

我认为这里的问题是 git 抛出的错误不能被 PS 捕获。

插图:

try 
    git push
    Write-Host "I run after the git push command"

catch 
    Write-Host "Something went wonky"

注意catch 块中缺少的Write-Host

这里我们需要查看git命令的退出代码。

在 PowerShell 中最简单的方法(我知道)是检查 $? 的值(有关 $? 的更多信息:What is `$?` in Powershell?)

try 
    git push
    if (-not $?) 
        throw "Error with git push!"
    
    Write-Host "I run after the git push command"

catch 
    Write-Host "Something went wonky"
    throw

检查我们的自定义错误(现在被catch 块捕获)!

【讨论】:

非常感谢您的解释!但是,就像我的 powershell 脚本中有很多行一样,我不能在每一行上都这样做,这将是不可读的...... 如果您关心可读性;创建一个为您执行“错误处理”的包装函数。功能比可读性更重要。但是,是的;可维护性应始终保持在显着位置! 如果有帮助,您可以单行检查:if ($?) throw

以上是关于带有 Git 命令错误处理的 Powershell - 在来自外部程序的非零退出代码时自动中止的主要内容,如果未能解决你的问题,请参考以下文章

尝试从批处理文件运行 PowerShell 命令时出现错误

批处理文件中的 Powershell:错误和命令未执行

带有 ssh 的 Git 克隆适用于 Linux,但不适用于 Windows PowerShell

使用windows powershell ISE管理命令窗口,并集成git命令

PhpStorm、Windows 10 PowerShell 和 Git 命令

Powershell 中的“Git Log”命令 - 无法终止进程