2

I'm trying to create a python script that starts a new window and waits for it to complete, and then retrieve the exit code. So I am using Popen with start /wait to create a separate window for it, but this does not properly forward the exit code.

This code summarizes the problem:

import subprocess

params = {}
params['stdout'] = subprocess.PIPE
params['stderr'] = subprocess.PIPE
params['shell'] = True

proc = subprocess.Popen('start /wait cmd /c exit 1', **params)

# This works but the above line does not
#proc = subprocess.Popen('exit 1', **params)

resultCode = proc.wait()

print(resultCode)

The documentation for start /wait suggests that it should return the exit code, and when I run it manually and then check %ERRORLEVEL% it appears correct, so I'm not sure what I'm doing wrong

Steve Vermeulen
  • 1,406
  • 1
  • 19
  • 25

2 Answers2

3

CMD's start command always succeeds overall if it successfully executes the given command via CreateProcess or ShellExecuteEx. It succeeds even if it's instructed to /wait and ends up setting %errorlevel% to a non-zero value. You can see this by running (start /wait exit 1) && echo success. The && operator only executes the right-hand expression if the left-hand expression succeeds.

To work around this, you can manually exit the initial shell using the !errorlevel! value that gets set by start. For example:

command = 'exit 1'
shell_path = os.environ.get('ComSpec', 'cmd.exe')
start_command = 'start "" /wait {}'.format(command)
cmdline = '"{shell}" /v:on /c "({command}) & exit !errorlevel!"'.format(
          shell=shell_path, command=start_command)

proc = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

Note that the above Popen call does not use the shell=True argument because the shell needs to be run manually with the /v:on option. This enables delayed expansion of environment variables, using "!" instead of "%".

That said, you don't need the shell for your stated objective. Simply have the child process create a new console by passing the CREATE_NEW_CONSOLE creation flag as follows:

proc = subprocess.Popen(args, creationflags=subprocess.CREATE_NEW_CONSOLE,
           stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Community
  • 1
  • 1
Eryk Sun
  • 33,190
  • 5
  • 92
  • 111
0

Using

proc = subprocess.Popen('call cmd /c exit 1', **params)

Instead of your start /wait returns the error code 1 in resultCode.

By the logic of this question and answer: CALL command vs. START with /WAIT option

My guess would be that the difference is that start /wait doesn't share variables with the same environment and call does.

Ofer Sadan
  • 11,391
  • 5
  • 38
  • 62