1

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? :(

2 Answers2

2

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.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Hi @mklement0 nice to see you again, thanks for helping me on my other question. However, in this instance your answer is off. In my test.ps1 I have: ```exit 10 ``` when I execute it: ```powershell -executionpolicy bypass .\test.ps1 echo %errorlevel% ``` guess what, I get 1. not 10 or whatever number I exit with. I only get 0 or 1. – gk_2000 May 25 '22 at 21:56
  • You're welcome, @gk_2000. To see a `.ps1` file's exit code, you must invoke it with `-File`: `powershell -executionpolicy bypass -File .\test.ps1`. See [this answer](https://stackoverflow.com/a/57468523/45375) for more information. – mklement0 May 25 '22 at 23:45
0

SHould have used !var! instead of %var%