Search code examples
pythonwindowsbatch-filesubprocess

Escaping quotes in Python subprocesses for Windows


I'm trying to terminate a Python program that uses many threads on its own.

If I'm not mistaken, just sys.exit() works fine.

However, to guard against my many mistakes, including losing references to threads, I tried the following:

subprocess.Popen(['start', 'cmd.exe', '/c', f'timeout 5&taskkill /f /fi "PID eq {os.getppid()}"'], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

I thought it was a problem with escaping the quotes, so I tried several things but failed. I gave up and did the following and it worked perfectly.

with open('exit_self.bat', 'w') as file:
  file.write(f'timeout 5&taskkill /f /fi "PID eq {os.getppid()}"&del exit_self.bat')
subprocess.Popen(['start', 'cmd.exe', '/c', 'exit_self.bat'], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

How can I do it without temp files? What did I miss? For reference, I used /k instead of /c option of cmd.exe to leave the window and check the error message in the window, it is as follows:

Waiting for 0 seconds, press a key to continue ...
ERROR: Invalid argument/option - 'eq'.
Type "TASKKILL /?" for usage.

I'm not sure if it will help, but I added echo to see the syntax of the command being executed:

subprocess.Popen(['start', 'cmd.exe', '/k', 'echo', f'timeout 5&taskkill /f /fi "PID eq {os.getppid()}"'], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

The result is:

"timeout 5&taskkill /f /fi \"PID eq 3988\""

Solution

  • From subprocess.Popen:

    args should be a sequence of program arguments or else a single string or path-like object.

    Run notepad.exe and be sure to not type in its window so "Untitled - Notepad" is the title. I'm using WINDOWTITLE eq Untitled* so I don't have to look up the PID. Use a single string for the command. This will open a window, countdown, and kill Notepad. Note that the entire /c parameter must be quoted so it is passed to the second command window.

    import subprocess
    import time
    
    p = subprocess.Popen('start cmd.exe /c "timeout 5&taskkill /f /fi "WINDOWTITLE eq Untitled*""', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    for i in range(3):
        print(str(i) * 10)
        time.sleep(1)
    
    

    Output in original command window showing parallel work. Another cmd window opens with the countdown. Notepad was killed 2 seconds after 2222222222 printed due to the 5 second total timeout before taskkill:

    0000000000
    1111111111
    2222222222