I suspect there is no good solution, but perhaps I'm overlooking something:
What I'm after is a way to:
(a) call a batch file from PowerShell in a way that robustly reflects its - implicit or explicit - exit code in PowerShell's automatic
$LASTEXITCODE
variable.Notably, calling a batch file that exits with, say,
whoami -nosuch || exit /b
, should result in$LASTEXITCODE
reflectingwhoami
's exit code, i.e.1
. This is not the case when you invoke a batch file (by name or path) from PowerShell: the exit code is0
(by contrast, inside acmd.exe
session%ERRORLEVEL%
is set to1
).Also note that the invocation should remain integrated with PowerShell's output streams, so I am not looking for solutions based on
System.Diagnostics.Process
.Furthermore, I have no knowledge of or control over the batch files getting invoked - I'm looking for a generic solution.
(b) without double-quoted arguments passed to the batch file getting altered in any way, and without
cmd.exe
's behavior getting modified in any way; notably:^
characters should not be doubled (see below).- Enabling delayed expansion with
/V:ON
is not an option.
The only way I know how to solve (a) is to invoke the batch file via cmd /c call
.
Unfortunately, this violates requirement (b), because the use of call
seemingly invariably doubles ^
characters in arguments. (And, conversely, not using call
then doesn't report the exit code reliably).
Is there a way to satisfy both requirements?
Note that PowerShell is only the messenger here: The problem lies with cmd.exe
, and anyone calling a batch file from outside a cmd.exe
session is faced with the same problem.
Example (PowerShell code):
# Create a (temporary) batch file that echoes its arguments,
# provokes an error, and exits with `exit /b` *without an explicit argument*.
'@echo off & echo [%*] & whoami -nosuch 2>NUL || exit /b' | Set-Content test.cmd
# Invoke the batch file and report the exit code.
.\test.cmd "a ^ 2"; $LASTEXITCODE
The output should be:
["a ^ 2"]
1
However, in reality the exit code is not reported:
["a ^ 2"]
0 # !! BROKEN
If I call with cmd /c call .\test.cmd
instead, the exit code is correct, but the ^
characters are doubled:
PS> cmd /c call .\test.cmd "a ^ 2"; $LASTEXITCODE
["a ^^ 2"] # !! BROKEN
1 # OK