How to tell if process is responding in Python on Windows - python

I am writing a python script to keep a buggy program open and I need to figure out if the program is not respoding and close it on windows. I can't quite figure out how to do this.

On Windows you can do this:
import os
def isresponding(name):
os.system('tasklist /FI "IMAGENAME eq %s" /FI "STATUS eq running" > tmp.txt' % name)
tmp = open('tmp.txt', 'r')
a = tmp.readlines()
tmp.close()
if a[-1].split()[0] == name:
return True
else:
return False
It is more robust to use the PID though:
def isrespondingPID(PID):
os.system('tasklist /FI "PID eq %d" /FI "STATUS eq running" > tmp.txt' % PID)
tmp = open('tmp.txt', 'r')
a = tmp.readlines()
tmp.close()
if int(a[-1].split()[1]) == PID:
return True
else:
return False
From tasklist you can get more information than that. To get the "NOT RESPONDING" processes directly, just change "running" by "not responding" in the functions given. See more info here.

Piling up on the awesome answer from #Saullo GP Castro, this is a version using subprocess.Popen instead of os.system to avoid creating a temporary file.
import subprocess
def isresponding(name):
"""Check if a program (based on its name) is responding"""
cmd = 'tasklist /FI "IMAGENAME eq %s" /FI "STATUS eq running"' % name
status = subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout.read()
return name in str(status)
The corresponding PID version is:
def isresponding_PID(pid):
"""Check if a program (based on its PID) is responding"""
cmd = 'tasklist /FI "PID eq %d" /FI "STATUS eq running"' % pid
status = subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout.read()
return str(pid) in str(status)
The usage of timeit showed that the usage of subprocess.Popen is twice as fast (mainly because we don't need to go through a file):
+-----------------------------+---------------------------+
| Function | Time in s (10 iterations) |
+-----------------------------+---------------------------+
| isresponding_os | 8.902 |
+-----------------------------+---------------------------+
| isrespondingPID_os | 8.318 |
+-----------------------------+---------------------------+
| isresponding_subprocess | 4.852 |
+-----------------------------+---------------------------+
| isresponding_PID_subprocess | 4.868 |
+-----------------------------+---------------------------+
Suprisingly, it is a bit slower for os.system implementation if we use PID but not much different if we use subprocess.Popen.
Hope it can help.

Related

Killing process by its command name [duplicate]

Is there any way I can get the PID by process name in Python?
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3110 meysam 20 0 971m 286m 63m S 14.0 7.9 14:24.50 chrome
For example I need to get 3110 by chrome.
You can get the pid of processes by name using pidof through subprocess.check_output:
from subprocess import check_output
def get_pid(name):
return check_output(["pidof",name])
In [5]: get_pid("java")
Out[5]: '23366\n'
check_output(["pidof",name]) will run the command as "pidof process_name", If the return code was non-zero it raises a CalledProcessError.
To handle multiple entries and cast to ints:
from subprocess import check_output
def get_pid(name):
return map(int,check_output(["pidof",name]).split())
In [21]: get_pid("chrome")
Out[21]:
[27698, 27678, 27665, 27649, 27540, 27530, 27517, 14884, 14719, 13849, 13708, 7713, 7310, 7291, 7217, 7208, 7204, 7189, 7180, 7175, 7166, 7151, 7138, 7127, 7117, 7114, 7107, 7095, 7091, 7087, 7083, 7073, 7065, 7056, 7048, 7028, 7011, 6997]
Or pas the -s flag to get a single pid:
def get_pid(name):
return int(check_output(["pidof","-s",name]))
In [25]: get_pid("chrome")
Out[25]: 27698
You can use psutil package:
Install
pip install psutil
Usage:
import psutil
process_name = "chrome"
pid = None
for proc in psutil.process_iter():
if process_name in proc.name():
pid = proc.pid
break
print("Pid:", pid)
you can also use pgrep, in prgep you can also give pattern for match
import subprocess
child = subprocess.Popen(['pgrep','program_name'], stdout=subprocess.PIPE, shell=True)
result = child.communicate()[0]
you can also use awk with ps like this
ps aux | awk '/name/{print $2}'
For posix (Linux, BSD, etc... only need /proc directory to be mounted) it's easier to work with os files in /proc.
It's pure python, no need to call shell programs outside.
Works on python 2 and 3 ( The only difference (2to3) is the Exception tree, therefore the "except Exception", which I dislike but kept to maintain compatibility. Also could've created a custom exception.)
#!/usr/bin/env python
import os
import sys
for dirname in os.listdir('/proc'):
if dirname == 'curproc':
continue
try:
with open('/proc/{}/cmdline'.format(dirname), mode='rb') as fd:
content = fd.read().decode().split('\x00')
except Exception:
continue
for i in sys.argv[1:]:
if i in content[0]:
print('{0:<12} : {1}'.format(dirname, ' '.join(content)))
Sample Output (it works like pgrep):
phoemur ~/python $ ./pgrep.py bash
1487 : -bash
1779 : /bin/bash
Complete example based on the excellent #Hackaholic's answer:
def get_process_id(name):
"""Return process ids found by (partial) name or regex.
>>> get_process_id('kthreadd')
[2]
>>> get_process_id('watchdog')
[10, 11, 16, 21, 26, 31, 36, 41, 46, 51, 56, 61] # ymmv
>>> get_process_id('non-existent process')
[]
"""
child = subprocess.Popen(['pgrep', '-f', name], stdout=subprocess.PIPE, shell=False)
response = child.communicate()[0]
return [int(pid) for pid in response.split()]
To improve the Padraic's answer: when check_output returns a non-zero code, it raises a CalledProcessError. This happens when the process does not exists or is not running.
What I would do to catch this exception is:
#!/usr/bin/python
from subprocess import check_output, CalledProcessError
def getPIDs(process):
try:
pidlist = map(int, check_output(["pidof", process]).split())
except CalledProcessError:
pidlist = []
print 'list of PIDs = ' + ', '.join(str(e) for e in pidlist)
if __name__ == '__main__':
getPIDs("chrome")
The output:
$ python pidproc.py
list of PIDS = 31840, 31841, 41942
if you're using windows,
you can get PID of process/app with it's image name with this code:
from subprocess import Popen, PIPE
def get_pid_of_app(app_image_name):
final_list = []
command = Popen(['tasklist', '/FI', f'IMAGENAME eq {app_image_name}', '/fo', 'CSV'], stdout=PIPE, shell=False)
msg = command.communicate()
output = str(msg[0])
if 'INFO' not in output:
output_list = output.split(app_image_name)
for i in range(1, len(output_list)):
j = int(output_list[i].replace("\"", '')[1:].split(',')[0])
if j not in final_list:
final_list.append(j)
return final_list
it will return you all PID of a app like firefox or chrome e.g.
>>> get_pid_of_app("firefox.exe")
[10908, 4324, 1272, 6936, 1412, 2824, 6388, 1884]
let me know if it helped
If your OS is Unix base use this code:
import os
def check_process(name):
output = []
cmd = "ps -aef | grep -i '%s' | grep -v 'grep' | awk '{ print $2 }' > /tmp/out"
os.system(cmd % name)
with open('/tmp/out', 'r') as f:
line = f.readline()
while line:
output.append(line.strip())
line = f.readline()
if line.strip():
output.append(line.strip())
return output
Then call it and pass it a process name to get all PIDs.
>>> check_process('firefox')
['499', '621', '623', '630', '11733']
Since Python 3.5, subprocess.run() is recommended over subprocess.check_output():
>>> int(subprocess.run(["pidof", "-s", "your_process"], stdout=subprocess.PIPE).stdout)
Also, since Python 3.7, you can use the capture_output=true parameter to capture stdout and stderr:
>>> int(subprocess.run(["pidof", "-s", "your process"], capture_output=True).stdout)
On Unix, you can use pyproc2 package.
Installation
pip install pyproc2
Usage
import pyproc2
chrome_pid=pyproc2.find("chrome").pid #Returns PID of first process with name "chrome"

Python: Returning 0 in min(list) with no min of 0

I'm creating a command that will take a partial or whole name match of a process and kill its lowest pid - and thus the rest of the processes spawned from it. My code returns a min(list_of_process_ids) of 0, of which there is no min of 0. Please enlighten me as to why this is happening. Thank you.
#!/usr/bin/env python
"""Kill proceses by partial name matching"""
import os, sys
def usage():
return ("pskill.py process_name")
def pids(proc):
""" Find the processes"""
procs = []
procs = os.system("ps -ef|grep -i " + proc + "|grep -v grep|grep -v pfind|awk '{print $2}'")
procs = [int(x) for x in str(procs)]
return procs
def kill(procs):
ppid = min(procs)
os.system("kill " + str(ppid))
return ("Processes Killed...")
def main():
if len(sys.argv) != 2:
print (usage())
else:
proc = sys.argv[1]
pids(proc)
kill(pids(proc))
main()
You aren't grabbing the stdout, so you aren't actually getting anything other that the exist status of the command. Which you can be glad is 0 :-)
Try using the subprocess module. Specifically with the stdout option of piping the result to your python console...

Why do I have two copies of the same script being runned by cron?

I have only one line devoted to the script I'm running every 15 minutes:
*/30 0-3,5-9,11-15,17-21,23 * * * ~/env/bin/python2 ~/hasoffers_api/script.py > ~/hasoffers_api/logs/script_`date +\%Y\%m\%d\%H\%M\%S`.log 2>&1
The script itself is secured internally from running twice if the first instance haven't stopped working yet (script is written in Python):
def checkPidRunning(pid):
global script_name
if pid<1:
print "Incorrect pid number!"
exit()
try:
os.kill(pid, 0)
except OSError:
print "Abnormal termination of previous process."
return False
else:
ps_command = "ps -p %s -o cmd h | grep %s" % (pid, script_name)
# ipdb.set_trace()
process_exist = os.system(ps_command)
if process_exist == 0:
return True
else:
print "Process with pid %s is not a Python process. Continue..." % pid
return False
if __name__ == '__main__':
script_name = os.path.basename(__file__)
pid = str(os.getpid())
pidfile = os.path.join("/", "tmp/", script_name + ".pid")
if os.path.isfile(pidfile):
print "Warning! Pid file %s existing. Checking for process..." % pidfile
r_pid = int(file(pidfile, 'r').readlines()[0])
if checkPidRunning(r_pid):
print "Python process with pid = %s is already running. Exit!" % r_pid
exit()
else:
file(pidfile, 'w').write(pid)
else:
file(pidfile, 'w').write(pid)
try:
doing_stuff()
finally:
os.unlink(pidfile)
The question is:
Why do I see the script being runned twice when I check it by ps aux | grep python:
pavel 1502 0.0 0.0 4388 824 ? Ss 09:00 0:00 /bin/sh -c ~/env/bin/python2 ~/hasoffers_api/scruot.py > ~/hasoffers_api/logs/script_`date +%Y%m%d%H%M%S`.log 2>&1
pavel 1541 26.9 1.9 2633524 159692 ? Sl 09:00 16:55 /home/pavel/env/bin/python2 /home/pavel/hasoffers_api/script.py
The only detail - I'm using ThreadPoolExecutor pattern inside of the script, but it's threads, not processes...

Extracting CPU use for specific process periodically

I have a running demo on a Linux server which consumes quite a bit of CPU and Memory usage. These parameters keep changing based on the load of the running demo. I want to extract the CPU usage and Memory usage periodically , i.e. every 3-4 sec and create a plot of the extracted result.
Considering the process as " Running Demo", on the terminal I typed:
ps aux |grep Running Demo | awk '{print $3 $4}'
This gives me the CPU and Memory usage of Running Demo. But I want the next two things i.e.
1) get this result outputted every 3-4 sec.
2) Make a plot of the generated result.
Any help or suggestion will be highly appreciated. I am a starter in this community.
Thanks
What you are trying to do is well known as an existing project :
See Munin
EXAMPLE
NOTE
it's supported by the Open Source community, so...
it will be stronger
don't run odd commands like ps aux |grep Running Demo | awk '{print $3 $4}' but ps auxw | awk '/Running Demo/{print $3 $4}'
many plugins exists and works for basics : CPU, RAM, FW, Apache and many more
if you really need gnuplot, see a top 3 on a goggle search http://blah.token.ro/post/249956031/using-gnuplot-to-graph-process-cpu-usage
the following python script accepts an output filename (png), and one or more pids. when you press ctrl-C it stops and uses gnuplot to generate a nice graph.
#!/usr/bin/env python
import os
import tempfile
import time
import sys
def total(pids):
return [sum(map(int, file('/proc/%s/stat' % pid).read().split()[13:17])) for pid in pids]
def main():
if len(sys.argv) == 1 or sys.argv[1] == '-h':
print 'log.py output.png pid1 pid2..'
return
pids = sys.argv[2:]
results = []
prev = total(pids)
try:
while True:
new = total(pids)
result = [(new[i]-prev[i])/0.1 for i, pid in enumerate(pids)]
results.append(result)
time.sleep(0.1)
prev = new
except KeyboardInterrupt:
pass
t1, t2 = tempfile.mkstemp()[1], tempfile.mkstemp()[1]
f1, f2 = file(t1, 'w'), file(t2, 'w')
print
print 'data: %s' % t1
print 'plot: %s' % t2
for result in results:
print >>f1, ' '.join(map(str, result))
print >>f2, 'set terminal png size %d,480' % (len(results)*5)
print >>f2, "set out '%s'" % sys.argv[1]
print >>f2, 'plot ' + ', '.join([("'%s' using ($0/10):%d with linespoints title '%s'" % (t1, i+1, pid)) for i, pid in enumerate(pids)])
f1.close()
f2.close()
os.system('gnuplot %s' % t2)
if __name__ == '__main__':
main()

How to get output from external command combine with Pipe

I have command like this.
wmctrl -lp | awk '/gedit/ { print $1 }'
And I want its output within python script, i tried this code
>>> import subprocess
>>> proc = subprocess.Popen(["wmctrl -lp", "|","awk '/gedit/ {print $1}"], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
>>> proc.stdout.readline()
'0x0160001b -1 6504 beer-laptop x-nautilus-desktop\n'
>>> proc.stdout.readline()
'0x0352f117 0 6963 beer-laptop How to get output from external command combine with Pipe - Stack Overflow - Chromium\n'
>>> proc.stdout.readline()
'0x01400003 -1 6503 beer-laptop Bottom Expanded Edge Panel\n'
>>>
It seem my code is wrong only wmctrl -lp was execute, and | awk '{print $1}' is omitted
My expect output would like 0x03800081
$ wmctrl -lp | awk '/gedit/ {print $1}'
0x03800081
Does one please help.
With shell=True, you should use a single command line instead of an array, otherwise your additional arguments are interpreted as shell arguments. From the subprocess documentation:
On Unix, with shell=True: If args is a string, it specifies the command string to execute through the shell. If args is a sequence, the first item specifies the command string, and any additional items will be treated as additional shell arguments.
So your call should be:
subprocess.Popen("wmctrl -lp | sed /gedit/ '{print $1}'", shell=True, ...
I think you may also have an unbalanced single quote in there.
Because you are passing a sequence in for the program, it thinks that the pipe is an argument to wmcrtrl, such as if you did
wmctrl -lp "|"
and thus the actual pipe operation is lost.
Making it a single string should indeed give you the correct result:
>>> import subprocess as s
>>> proc = s.Popen("echo hello | grep e", shell=True, stdout=s.PIPE, stderr=s.PIPE)
>>> proc.stdout.readline()
'hello\n'
>>> proc.stdout.readline()
''
After some research, I have the following code which works very well for me. It basically prints both stdout and stderr in real time. Hope it helps someone else who needs it.
stdout_result = 1
stderr_result = 1
def stdout_thread(pipe):
global stdout_result
while True:
out = pipe.stdout.read(1)
stdout_result = pipe.poll()
if out == '' and stdout_result is not None:
break
if out != '':
sys.stdout.write(out)
sys.stdout.flush()
def stderr_thread(pipe):
global stderr_result
while True:
err = pipe.stderr.read(1)
stderr_result = pipe.poll()
if err == '' and stderr_result is not None:
break
if err != '':
sys.stdout.write(err)
sys.stdout.flush()
def exec_command(command, cwd=None):
if cwd is not None:
print '[' + ' '.join(command) + '] in ' + cwd
else:
print '[' + ' '.join(command) + ']'
p = subprocess.Popen(
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd
)
out_thread = threading.Thread(name='stdout_thread', target=stdout_thread, args=(p,))
err_thread = threading.Thread(name='stderr_thread', target=stderr_thread, args=(p,))
err_thread.start()
out_thread.start()
out_thread.join()
err_thread.join()
return stdout_result + stderr_result
When needed, I think it's easy to collect the output or error in a string and return.

Categories