0

UPDATE: Solved, see comment

I'm trying to run a batch file inside a powershell script, where I need to pass two arguments and read the return code.

I tried and read $LASTEXITCODE, but it always returns me "0".

This doesn't pass the arguments to the batch file:

& $BATCH_PATH $REQUIRED_PARAMETERS $OPTIONAL_PARAMETERS | Out-File -FilePath "$COMPARE_DIR\$LOG_FILENAME.log" -Append

This works, but LASTEXITCODE is always "0":

cmd.exe /c "$($ROOT_DIR)\batch.bat $REQUIRED_PARAMETERS $OPTIONAL_PARAMETERS" | Out-File -FilePath "$COMPARE_DIR\$LOG_FILENAME.log" -Append

This works, but LASTEXITCODE is always "0":

Start-Process -FilePath "C:\Windows\System32\cmd.exe" -ArgumentList "/c", "$BATCH_PATH", "$REQUIRED_PARAMETERS", "$OPTIONAL_PARAMETERS" -WindowStyle Hidden -PassThru -Wait -RedirectStandardOutput "$COMPARE_DIR\$(Get-Random).log"

I also tried to get the ExitCode-Property of the Process-Object, but it is also 0.

LoopAt
  • 83
  • 1
  • 6

1 Answers1

2

tl;dr

Rather than having to modify your batch file by adding an exit /b %ERRORLEVEL% statement, you can modify the batch file's invocation by appending & exit in order to achieve the same effect:

cmd.exe /c "$($ROOT_DIR)\batch.bat $REQUIRED_PARAMETERS $OPTIONAL_PARAMETERS & exit" | 
  Out-File -FilePath "$COMPARE_DIR\$LOG_FILENAME.log" -Append

This obscure solution is necessitated by the unfortunate fact that cmd.exe doesn't reliably relay a batch file's exit code as its process exit code when the batch file is called from the outside, neither when a batch file is called directly nor via cmd.exe /c - see this answer for details.

Note:

  • The ie function that comes with the Native module (Install-Module Native) automatically applies this workaround, so that invocation as
    ie "$($ROOT_DIR)\batch.bat" ... would work as-is with respect to setting $LASTEXITCODE (though you'd have to pass the arguments individually or via array variables, as discussed).

  • GitHub proposal #15143 advocates building this workaround into PowerShell itself, as part of a larger proposal to improve argument-passing to external programs on Windows, but the proposal was rejected.


As for what you tried:

This doesn't pass the arguments to the batch file:

& $BATCH_PATH $REQUIRED_PARAMETERS $OPTIONAL_PARAMETERS`

This syntax works in principle, but not if your variables contain multiple arguments as a single string. E.g., $REQUIRED_PARAMETERS = 'foo bar' would not work, but specifying the arguments a an an array of strings would, $REQUIRED_PARAMETERS = 'foo', 'bar': In the former case, 'foo bar' is passed as a single argument, in the latter case, the array elements become individual arguments.

This works, but LASTEXITCODE is always "0":

Start-Process -FilePath "C:\Windows\System32\cmd.exe" -ArgumentList "/c", "$BATCH_PATH", "$REQUIRED_PARAMETERS", "$OPTIONAL_PARAMETERS" -WindowStyle Hidden -PassThru -Wait -RedirectStandardOutput "$COMPARE_DIR\$(Get-Random).log"

I also tried to get the ExitCode-Property of the Process-Object, but it is also 0.

The automatic $LASTEXITCODE variable is only set for direct calls to external programs, not when you use Start-Process - which is generally the wrong tool for invoking console applications.

(As your statement implies, it is possible to get a process' exit code when Start-Process is used, namely by adding -Wait -PassThru to the call in order to wait for the process to exit and to return a System.Diagnostics.Process instance whose exit code can then be inspected.

In the case at hand this wouldn't make a difference anyway, because the problem lies with cmd.exe itself, not with how it is invoked.)

mklement0
  • 382,024
  • 64
  • 607
  • 775