How to know if an external process has finished in python? - python

I am working from a windows platform. In my python script, I can make a call to an external program in the following way:
os.system("C:\mainfolder\menu.exe C:\others\file1.inp C:\others\file2.inp")
os.popen("C:\mainfolder\menu.exe C:\others\file1.inp C:\others\file2.inp")
subprocess.call(["C:\mainfolder\menu.exe","C:\others\file1.inp" "C:\others\file2.inp"])
where:
menu.exe: is my external program.
file1 and file2: are input files to my external program.
All the above works fine. Now that my external program has finished successfully, I need to totally close it along with all the windows that are left opened by it. I have gone through lots of other posts, python documentation, etc and found commands as for example:
os.system("taskkill /im C:\mainfolder\menu.exe")
os.kill(proc.pid,9)
child.kill()
But they did not work. I spent a lot of time trying to find something that worked for me, until I realised that no matter which commands I type after, they will not be read as the computer does not know that my external program has finished. That is the reason why I can easily terminate the program from the command line anytime just by typing taskkill /im menu.exe, but not from python.
Does anybody know how to sort this out?, should I include something else when I make the call to my external program?

Here's some example code, how to detect if a program opens a window. All you need to know is the title of the message box, that menu.exe opens, when it is finished:
import subprocess
import win32gui
import time
def enumHandler(hwnd, lParam):
if win32gui.IsWindowVisible(hwnd):
if 'Menu.exe Finished' in win32gui.GetWindowText(hwnd):
proc.kill()
proc = subprocess.Popen(["C:\mainfolder\menu.exe","C:\others\file1.inp" "C:\others\file2.inp"])
while proc.poll() is None:
win32gui.EnumWindows(enumHandler, None)
time.sleep(1)

If you want to have a process end immediately, i.e., wait for it to end, this is a blocking call, and os.system() normally waits, discussed here as well as .communicate[0] using subprocess there.
If you want to end a process later in your program, an asynchronous, non-blocking process, perhaps get its pid and depending on whether shell=True or not that will either be the pid of the spawned shell or of the child process.
That pid can be used to end it either immediately by using psutil or os, or wait until it ends using little cpu time, though then other tasks can be done while waiting, or threads could be used.

It might be a bit late to post my findings to this question as I asked it some months back but it may still be helpful for other readers.
With the help of the people who tried answering my question, especially Daniel's answer, I found out a solution for my problem. Not sure if it is the best one but I got what I was needing.
Basically instead of looking for the word "finished" on my pop up window, I looked for the results that my external program generates. If these results have been generated, it means that the program has finished so I then kill the process:
proc=subprocess.Popen(["C:\mainfolder\menu.exe","C:\others\file1.inp" "C:\others\file2.inp"])
while proc.poll() is None:
if os.path.exists("D:\\Results_folder\\Solution.txt"):
time.sleep(10)
os.system('taskkill /im menu.exe')

Related

Is there a way I can store the output of a terminal command into a file using python?

I want to store the output of the terminal command top into a file, using Python.
In the terminal, when I type top and hit enter, I get an output that is real time, so it keeps updating. I want to store this into a file for a fixed duration and then stop writing.
file=open("data.txt","w")
file.flush()
import os,time
os.system("top>>data.txt -n 1")
time.sleep(5)
exit()
file.close()
I have tried to use time.sleep() and then exit(), but it doesn't work, and the only way top can be stopped is in the terminal, by Control + C
The process keeps running and the data is continuously written onto the file, which is not ideal, as one would guess
For clarity: I know how to write the output on to the file, I just want to stop writing after a period
system will wait for the end of the child process. If you do not want that, the Pythonic way is to directly use the subprocess module:
import subprocess
timeout=60 # let top run for one minute
file=open("data.txt","w")
top = subprocess.Popen(["top", "-n", 1], stdout=file)
if top.wait(timeout) is None: # wait at most timeout seconds
top.terminate() # and terminate child
The panonoic way (which is highly recommended for robust code) would be to use the full path of top. I have not here, because it may depend on the actual system...
The issue you could be facing is that os.system starts the process as part of the current process. So the rest of your script will not be run until the command you run has completed execution.
I think what you want to be doing is executing your console command on another thread so that the thread running your python script can continue while the command runs in the background. See run a python program on a new thread for more info.
I'd suggest something like (this is untested):
import os
import time
import multiprocessing
myThread = multiprocessing.process(target=os.system, args=("top>>data.txt -n 1",))
myThread.start()
time.sleep(5)
myThread.terminate()
That being said, you may need to consider the thread safety of os.system(), if it is not thread safe you'll need to find an alternative that is.
Something else worth noting (and that I know little about) is that it may not be ideal to terminate threads in this way, see some of the answers here: Is there any way to kill a Thread?

Close Image with subprocess

I am trying to open an image with subprocess so it is visible to the user, then close the image so that it disapears.
This question has been asked before, but the answers I found have not worked for me. Here is what I have checked:
Killing a process created with Python's subprocess.Popen()
How to terminate a python subprocess launched with shell=True
How can I close an image shown to the user with the Python Imaging Library?
I need the code to open an image (with Preview (optional), default on my Mac), wait a second, then close the image.
openimg = subprocess.Popen(["open", mypath])
time.sleep(1)
openimg.terminate()
openimg.kill()
Everything is telling me to terminate() and kill(), but that still isn't closing the image.
This does not HAVE to use preview, I am open to hearing other options as well.
Edit: I have additionally tried the below code, still no success
print('openimg\'s pid = ',openimg.pid)
os.kill(openimg.pid, signal.SIGKILL)
OS X's open command is asynchronous - it spawns a Preview process (or whatever app it launches) and immediately exits. So by the time your Python code gets around to calling terminate() and kill(), the open process is done. It no longer exists.
You can force synchronous behavior, i.e. make open keep running until after Preview exits, by passing the -W option,
subprocess.Popen(["open", "-W", mypath])
This way, open will still be running when your code gets around to running terminate and kill. (I would suggest also passing the -n option to make sure Preview starts a new instance, in case you had another instance of Preview sitting around from before.) And when the open process ends, hopefully it will also end the underlying Preview process. You can check whether this actually happens using a process viewer such as ps or pgrep.
If terminating or killing open does not kill Preview, you'll need to either change the configuration so that the signal is delivered to all subprocesses of open when you call terminate() or kill(), for which this question or this one may be helpful, or you'll have to find a way to get the process ID of Preview and send signals to that directly, which will require you to go beyond Popen. I'm not sure of the best way to do that, but perhaps someone else can contribute an answer that shows you how.
I'm sure this is the hackiest way to do this, but hell, it works. If anyone stumbles on this and knows a better way, please let me know.
The Problem
As David Z described, I was not targeting the correct process. So I had to figure out what the correct process ID was in order to close it properly.
I started by opening my terminal and entering the command ps -A. This will show all of the current open processes and their ID. Then I just searched for Preview and found two open processes.
Previously, I was closing the first Preview pid in the list, let's call it 11329 but the second on the list was still open. The second Preview process, 11330, was always 1 digit higher then the first process. This second one was the pid I needed to target to close Preview.
The Answer
openimg = subprocess.Popen(["open", mypath]) ##Opens the image to view
newpid = openimg.pid + 1 ##Gets the pid 1 digit higher than the openimg pid.
os.kill(newpid, signal.SIGKILL) ##Kills the pid and closes Preview
This answer seems very fragile, but it works for me. I only just started learning about pids so if anyone could provide some knowledge, I would be grateful.

Python check if another script is running and stop it

I have several python scripts that turn my TV on and off. Sometimes the TV does not respond the first time so I use a while loop to continue sending the command until the "success" response is sent. Up to 10 times.
I need to check if one of these programs are running when any of them are started and kill the first process.
This answer uses domain locks and I think this could work but I dont really understand whats happening there:
https://stackoverflow.com/a/7758075/2005444
What I dont know is what the process_name would be. The scripts are titles tvon.py, tvoff.py, and tvtoggle.py. Is it just the title? Would it include the extension? How do I get the pid so I can kill the process?
This is running on Ubuntu 14.04.1
EDIT: all I really need is to search for any of these running scripts first. Also, instead of killing the process maybe I could just wait for it to finish. I could just do a loop and break it if none of those processes are running.
The reason I need to do this is if the tv is off and the off script is run it will never succeed. The TV wont respond if it is already off. Which is why I built in the limit of 10 commands. It never really takes over 4 so 10 is overkill. The problem is if the off command is trying to run and I turn the TV on using the tvon script the TV will turn on and back off. Although the TV limits how often commands can be accepted, which reduces the chance of this happening I still want the to be as cleanly working as possible.
EDIT:
I found that I can not kill the process because it can lock the tty port up which requires a manual restart. So I think the smarter way is to have the second process wait until the first is done. Or find a way to tell the first process to stop at a specific point in the loop so I know its not transmitting.
If you have a socket, use it. Sockets provide full-blown bidirectional communication. Just write your script to kill itself if it receives anything on the socket. This can be most easily done by creating a separate thread which tries to do a socket.recv() (for SOCK_DGRAM) or socket.accept() (for SOCK_STREAM/SOCK_SEQPACKET), and then calls sys.exit() once that succeeds.
You can then use socket.send() (SOCK_DGRAM) or socket.connect() (SOCK_STREAM/SOCK_SEQPACKET) from the second script instance to ask the first instance to exit.
This function can kill a python script by name on *nix systems. It looks through a list of running processes, finds the PID of the one associated with your script, and issues a kill command.
import subprocess
def killScript(scriptName):
# get running processes with the ps aux command
res = subprocess.check_output(["ps","aux"], stderr=subprocess.STDOUT)
for line in res.split("\n"):
# if one of the lines lists our process
if line.find(scriptName) != -1:
info = []
# split the information into info[]
for part in line.split(" "):
if part.strip() != "":
info.append(part)
# the PID is in the second slot
PID = info[1]
#kill the PID
subprocess.check_output(["kill",PID], stderr=subprocess.STDOUT)
At the beginning of your tv script you could run something like:
killList = ["tvon.py", "tvoff.py", "tvtoggle.py"]
for script in killList:
killScript(script)

How can I inspect a linux process to determine how/when a process dies/terminates?

I have a python irc bot which I start up as root by doing /etc/init.d/phenny start. Sometimes it dies, though and it seems to happen overnight.
What can I do to inspect it and see the status of the process in a text file?
If you know it's still running, you can pstack it to see it's walkback. I'm not sure how useful that will be because you will see the call stack of the interpreter. You could also try strace or ltrace as someone else mentioned.
I would also make sure that in whatever environment the script runs in, you have set ulimit -c unlimited so that a core is generated in case python it is outright crashing.
Another thing I might try is to have this job executed by a parent that does not wait it's child. This should cause the proc table entry to stick around as a zombie even when the underlying job has exited.
If you're interested in really low level process activity, you can run the python interpreter under strace with standard error redirected to a file.
If you're only interested in inspecting the python code when your bot crashes, and you have the location in the source where the crash happens, you can wrap that location with try/except and break into the debugger in theexcept clause:
import pdb; pdb.set_trace()
You'll probably need to run your bot in non-daemon mode for that to work, though.
You might want to try Python psutils, it is something that I have used and works.
A cheap way to get some extra clues about the problem would be to start phenny with
/etc/init.d/phenny start 2>/tmp/phenny.out 1>&2
When it crashes, check the tail of /tmp/phenny.out for the Python traceback.
If you only need to verify that the process is running you could just run a script that checks the output of command
ps ax | grep [p]henny
every few seconds. If it's empty, then obviously the process is dead.

How to find out if a program crashed with subprocess?

My application creates subprocesses. Usually, these processeses run and terminate without any problems. However, sometimes, they crash.
I am currently using the python subprocess module to create these subprocesses. I check if a subprocess crashed by invoking the Popen.poll() method. Unfortunately, since my debugger is activated at the time of a crash, polling doesn't return the expected output.
I'd like to be able to see the debugging window(not terminate it) and still be able to detect if a process is crashed in the python code.
Is there a way to do this?
When your debugger opens, the process isn't finished yet - and subprocess only knows if a process is running or finished. So no, there is not a way to do this via subprocess.
I found a workaround for this problem. I used the solution given in another question Can the "Application Error" dialog box be disabled?
Items of consideration:
subprocess.check_output() for your child processes return codes
psutil for process & child analysis (and much more)
threading library, to monitor these child states in your script as well once you've decided how you want to handle the crashing, if desired
import psutil
myprocess = psutil.Process(process_id) # you can find your process id in various ways of your choosing
for child in myprocess.children():
print("Status of child process is: {0}".format(child.status()))
You can also use the threading library to load your subprocess into a separate thread, and then perform the above psutil analyses concurrently with your other process.
If you find more, let me know, it's no coincidence I've found this post.

Categories

Resources