Aniruddha Gore
Aniruddha Gore

Reputation: 67

Capture exit code from a powershell script in a cmd file

I have a simple PS script:

    [CmdletBinding()]
    param(
      [Parameter()]
      [String] $CodeCoverageXmlReportPath,
      
      [Parameter()]
      [Int32] $MinimumLineCoverage = 100
    )
    
    Write-Host $"MinimumLineCoverage = $MinimumLineCoverage"
    Write-Host $"Reading CodeCoverage XML report: {$CodeCoverageXmlReportPath}"
    
    ### logic to calculate code coverage from report ###
    
    if ($lineCoverageNum -lt $MinimumLineCoverage)
    {
       Write-Host $"Code coverage of this build ($lineCoverageNum) is below the expected value ($MinimumLineCoverage). Please add tests to validate new code."
       exit 1
    }
    else
    {
       Write-Host $"Code coverage check (($lineCoverageNum)) for this build is at par with the ($MinimumLineCoverage). Great job!."
       exit 0
    }

These are the two ways I have tried to execute it from a .cmd file:

%SystemRoot%\SysWOW64\WindowsPowerShell\v1.0\powershell.exe -NonInteractive -ExecutionPolicy Unrestricted -File ValidateCodeCoverage.ps1 -CodeCoverageXmlReportPath "%TestResultsDir%\Cobertura.xml" -MinimumLineCoverage 57
    
ECHO ErrorLevel=%errorlevel%

and

%SystemRoot%\SysWOW64\WindowsPowerShell\v1.0\powershell.exe -NonInteractive -
    ExecutionPolicy Unrestricted -Command "& {.\ValidateCodeCoverage.ps1 -CodeCoverageXmlReportPath "%TestResultsDir%\Cobertura.xml" -MinimumLineCoverage 57; exit $LASTEXITCODE}"
    
ECHO ErrorLevel=%errorlevel%

In both these cases, checking the value of %errorlevel% right after the .ps1 execution remains 0 no matter what. I tried tested it with always exiting with return code 1, using trap, throwing an exception, etc. The %errorlevel% in cmd file is always set to 0.

What could I be missing? :(

Upvotes: 1

Views: 2082

Answers (2)

mklement0
mklement0

Reputation: 437052

Your PowerShell commands correctly set the exit code, so the problem must be in the way your batch file checks it.

The only scenario in which your code doesn't work is if it is inside a (...) block, where variable references such as %errorlevel% are expanded before the commands in the block execute; e.g.:

@echo off

REM # Does NOT work as expected, because %ErrorLevel% is expanded 
REM # BEFORE the powershell command excutes.
for /l %%i in (1,1,1) do (
  powershell.exe -ExecutionPolicy Bypass -Command "exit 5"
  echo ErrorLevel=%ErrorLevel%
)

Output is ErrorLevel=0, namely the value of %errorlevel% before the PowerShell command, which sets the exit code (error level) to 5, is executed.

To solve this problem:

  • You must use setlocal enableDelayedExpansion at the start of your batch file...

    • Note: The setlocal part implies that whatever custom variables you set in your batch file will be local to your batch file, which is generally preferable anyway; see setlocal /?
  • ... and then use !ErrorLevel! rather than %ErrorLevel%, because only the former guarantees dynamic expansion of the error level (exit code):

@echo off
setlocal enableDelayedExpansion

REM # OK - thanks to enableDelayedExpansion and !...!,
REM # powershell.exe's exit code is dynamically evaluated.
for /l %%i in (1,1,1) do (
  powershell.exe -ExecutionPolicy Bypass -Command "exit 5"
  echo ErrorLevel=!ErrorLevel!
)

Output is ErrorLevel=5, which now correctly reflects the PowerShell command's exit code.

Upvotes: 2

Aniruddha Gore
Aniruddha Gore

Reputation: 67

SHould have used !var! instead of %var%

Upvotes: 0

Related Questions