Search code examples
pythonwindowscommand-linepopenexit-code

Cannot get exit code when running subprocesses in python using Popen and start /wait


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


Solution

  • 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)