4

I'm using python 3.6 in Windows, and my aim is to run a cmd command and save the output as a string in a variable. I'm using subprocess and its objects like check_output, Popen and Communicate, and getoutput. But here is my problem with these:

subprocess.check_output the problem is if the code returns non-zero it raises an exception and I can't read the output, for example, executing the netstat -abcd.

stdout_value = (subprocess.check_output(command, shell=True, stdin=subprocess.PIPE, stderr=subprocess.DEVNULL, timeout=self.timeout)).decode()

subprocess.Popen and communicate() the problem is some commands like netstat -abcd returns empty from communicate().

self.process = subprocess.Popen(command, shell=True,
                                            stdout=subprocess.PIPE,
                                            stderr=subprocess.DEVNULL,
                                            stdin=subprocess.PIPE)
try:
   self.process.wait(timeout=5)
   stdout_value = self.process.communicate()[0]
except:
   self.process.kill()
   self.process.wait()

subprocess.getoutput(Command) is ok but there is no timeout so my code would block forever on executing some commands like netstat. I also tried to run it as a thread but the code is blocking and I can't stop the thread itself.

stdout_value = subprocess.getoutput(command)

What I want is to run any cmd commands (blocking like netstat or nonblocking like dir) with timeout for example if the user executes netstat it only shows the lines generated in timeout and then kills it. Thanks.

EDIT------ According to Jean's answer, I rewrote the code but the timeout doesn't work in running some commands like netstat.

# command = "netstat"
command = "test.exe"  # running an executable program can't be killed after timeout
self.process = subprocess.run(command, shell=True,
                              stdout=subprocess.PIPE,
                              stderr=subprocess.PIPE,
                              stdin=subprocess.PIPE,
                              timeout=3,
                              universal_newlines=True
                              )
stdout_value = self.process.stdout
Masoud Rahimi
  • 5,785
  • 15
  • 39
  • 67
  • ... it seems `netstat` has an 'internal buffer' or something and writes to stdout when it finishes ?! When executing `netstat > netstat.log` and the process is killed nothing is written to the log file. – Maurice Meyer Nov 04 '17 at 10:09
  • @MauriceMeyer I don't think so, I tried to run exe like "test.exe" but after running the exe file it didn't kill the process after timeout. – Masoud Rahimi Nov 04 '17 at 10:13
  • 1
    `netstat -abcd` takes longer than 3-5 seconds to finish. Thus the process ends up being killed and the output thrown away. What makes sense depends on your precise use case. The simplest solution by far is to run with a longer timeout so that it gets a chance to finish properly. – tripleee Feb 09 '23 at 10:54
  • 1
    https://stackoverflow.com/questions/4256107/running-bash-commands-in-python/51950538#51950538 has a longer discussion of what these functions do and what their differences are, though it doesn't currently cover `getoutput` – tripleee Feb 09 '23 at 10:56

1 Answers1

2

subprocess.run() with timeout doesn't seem to run properly on Windows.

You can try running the subprocess within a Timer-thread or in case of you dont need communicate(), you can do something like this:

import time
import subprocess

#cmd = 'cmd /c "dir c:\\ /s"' 
#cmd = ['calc.exe']
cmd = ['ping', '-n', '25', 'www.google.com']

#_stdout = open('C:/temp/stdout.txt', 'w')
#_stderr = open('C:/temp/stderr.txt', 'w')

_stdout = subprocess.PIPE
_stderr = subprocess.PIPE

proc = subprocess.Popen(cmd, bufsize=0, stdout=_stdout, stderr=_stderr)

_startTime = time.time()

while proc.poll() is None and proc.returncode is None:
    if (time.time() - _startTime) >= 5:
        print ("command ran for %.6f seconds" % (time.time() - _startTime))
        print ("timeout - killing process!")
        proc.kill()
        break

print (proc.stdout.read())

It works for all three commands on Win7/py3.6, but not for the 'killed-netstat' issue!

Maurice Meyer
  • 17,279
  • 4
  • 30
  • 47