1

I have tried at least a dozen different python scripts to kill a child process presented here on SO including

this and this

but am being extremely frustrated.

here is the python:

import subprocess, os
import time,signal,psutil
process = subprocess.Popen(['open', '/Applications/Preview.app', 'images/conv1.jpg'], shell=False)
print (process.pid)
time.sleep(2)
print (process.pid)
os.kill(process.pid, signal.SIGKILL)

The process id is printed in terminal, preview starts and opens an image, the process id is printed again, the python terminates and I am back at the shell prompt - but Preview and the image are still open. When I check the process id in 'Activity Monitor', It turns out the ACTUAL process id of preview.app is one greater than the value, process.pid.

I have to change the last line to this:

os.kill(process.pid+1, signal.SIGKILL)

and it works. Why????

aquagremlin
  • 3,515
  • 2
  • 29
  • 51
  • if i get rid of the last line so preview does not terminate and also comment out the time.sleep(2), then the ACTUAL process id is 5 greater than the value printed. What is this weirdness? – aquagremlin May 03 '19 at 01:21
  • Are you certain it's not being launched by something else? Also, your first link is using `os.killpg`, not `os.kill`. – jpmc26 May 03 '19 at 01:55

1 Answers1

2

Simply doing process.pid + 1 won't kill that process, it just happened that at that moment your child forked from it's parent, without other process starting.
Your process.pid is not the actual pid of your image conv1.jpg.So we need to find it's true pid:

import subprocess
import os
import time
import signal
process = subprocess.Popen(['xdg-open', 'stroke.png'])#I have linux machine and stroke.png is a file which I need to open.
print(process.pid)
time.sleep(5)
print(process.pid)
a = subprocess.Popen(['ps', '-eo', 'pid,ppid,command'], stdout = subprocess.PIPE)
b = subprocess.Popen(['grep', 'stroke.png'], stdin = a.stdout, stdout = subprocess.PIPE)

output, error  = b.communicate()
output = output.decode("utf-8").split('\n')
pid = ''
pid = int(pid.join(list(output[0])[1:5]))
print(pid)
os.kill(pid, signal.SIGKILL)

Here what we do is we take two process a and b.
a gives all the pid's, so we need to filter out the pid's for our file in my case stroke.png in process b using grep.
We give stdout of a to stin of b and then stdout of b to output.
We need to decode output to utf-8 because it returns in bytes and we need it in string.

print(output)

Gives us the following result:

[' 7990  1520 eog /home/rahul/stroke.png', ' 8004  7980 grep stroke.png', '']

So we need the number 7990 which is the true pid of our stroke.png.
This is taken by using int(pid.join(list(output[0])[1:5])) which gives us the numbers from position 1 to position 4 in our string which is at position 0 in list output then we join() them and wrap them in int because to kill pid we need an integer.
The output my program gives is:

rahul@RNA-HP:~$ python3 so5.py
7982
7982
7990

Here 7982 was the pid of our subprocess and 7990 is the pid of our stroke.png
Hope it helps
Comment if anything can be improved.

Rahul
  • 576
  • 1
  • 5
  • 9
  • I think it is `-Ao` instead of `-eo` for macOS users, for listing process. – Rahul May 03 '19 at 08:25
  • Quick side note (and I know that you copied it from the OP): using SIGKILL to instruct a process to terminate is almost never justified. SIGKILL is to be used as a last resort when a process refuses to exit upon receiving SIGTERM. SIGKILL is an order for immediate termination; the receiving process cannot execute routines for a clean shutdown (e.g. closing connections, finishing pending transactions, saving unsaved data). Hence it may cause data corruption. Better send SIGTERM. – shmee May 03 '19 at 09:01
  • Yes I generally use -9 which is SIGTERM and for such processes too. Thanks for pointing it out. – Rahul May 03 '19 at 09:03
  • 1
    Uh, no :) 9 _is_ SIGKILL. `kill ` sends SIGTERM (15) if [no signal is explicitly specified](http://man7.org/linux/man-pages/man1/kill.1.html). [Here's](https://unix.stackexchange.com/questions/317492/list-of-kill-signals) a list of signal numbers. – shmee May 03 '19 at 09:12
  • I must have been confused again Thank you. – Rahul May 03 '19 at 09:28
  • Thank you Rahul. Interestingly, I get 4 processes opening - all of them named after my image but none of 'xdg-open'. I thought perhaps xdg-open is one of those terminal only applications that destroys itself after it runs and pipes its output to a window so I tried the same thing with gimp. This is what I got: – aquagremlin May 03 '19 at 17:27
  • python3 termProc3.py 8900 ln: failed to create symbolic link '/home/stefan/snap/gimp/165/.config/gtk-2.0/gtkfilechooser.ini': File exists Gtk-Message: 13:24:48.760: Failed to load module "gail" Gtk-Message: 13:24:48.760: Failed to load module "atk-bridge" Gtk-Message: 13:24:48.763: Failed to load module "canberra-gtk-module" /snap/gimp/165/usr/bin/gimp: Gimp-Widgets-WARNING: parse_iso_codes: error parsing '/build/gimp/parts/gimp/install/usr/share/xml/iso-codes/iso_639.xml': No such file or directory – aquagremlin May 03 '19 at 17:28
  • gimp_device_info_set_device: trying to set GdkDevice 'Razer Razer BlackWidow Chroma' on GimpDeviceInfo which already has a device Gtk-Message: 13:24:50.375: Failed to load module "gail" Gtk-Message: 13:24:50.376: Failed to load module "atk-bridge" Gtk-Message: 13:24:50.378: Failed to load module "canberra-gtk-module" 8900 8900 stefan@4930:~/Dropbox/aPython/aSubprocessExamples$ (script-fu:9033): LibGimpBase-WARNING **: 13:24:53.386: script-fu: gimp_wire_read(): error – aquagremlin May 03 '19 at 17:28
  • sorry the formatting sucks in these comments but basically 20 processes appeared, they all were named after my image. Two process ids printed out in terminal and this number was the same of the process id that was listed in htop in white. all the other differently numbered processes in htop were green. I appreciate the time you took to reply. – aquagremlin May 03 '19 at 17:36
  • So to really understand what you said Rahul, I tried to implement this idea: search for the name of the image in the list of running processes. Here is the code i used: – aquagremlin May 03 '19 at 17:42
  • `import subprocess, os import time,signal,psutil` `process = subprocess.Popen(['xdg-open', 'images/conv1.jpg'], shell=False)` `time.sleep(1)` `for proc in psutil.process_iter():` ` try:` ` pinfo = proc.as_dict(attrs=['pid', 'name', 'username'])` ` except psutil.NoSuchProcess:` ` pass` ` else:` ` if pinfo['name']=='conv1.jpg':` ` print(pinfo)` ` `print (process.pid)` – aquagremlin May 03 '19 at 17:43
  • @aquagremlin `xdg-open` is for linux users like me. You use `open` which is for mac users and also `-Ao` instead of `-eo`. – Rahul May 03 '19 at 17:50
  • basically i used psutil to iterate through all the processes to find the name of the image. it didnt work. the process id i found printed in the terminal was not listed in htop. ?? – aquagremlin May 03 '19 at 17:54
  • i'm doing this on a linux box now to replicate what you did. your code works fine. my use of psutil fails. – aquagremlin May 03 '19 at 17:55
  • Try sleeping for 5-10 seconds, sleeping for such amount of time is not good imo but I don't have any other solution. I also had some other stuff with pid's and whenever the time between initiating subprocess of python and the search for pid was too less it just would not work. – Rahul May 03 '19 at 17:56