Check if Python Script is already running

26,806

Solution 1

My current solution looks like this (on OSX). I am using pgrep in combination with regexp

import subprocess

cmd = ['pgrep -f .*python.*testing03.py']
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, 
stderr=subprocess.PIPE)
my_pid, err = process.communicate()

if len(my_pid.splitlines()) >0:
   print("Running")
   exit()
else:
  print("Not Running")

As far as i have tested this should work even if the python string differs. Location of the script doesn't matter either

Solution 2

Determining if a python script is running using your pgrep based method is not reliable. For example:

> ps -ef | grep 'python sleep.py'
userid  21107  2069  0 12:51 pts/3    00:00:00 python sleep.py
userid  21292  2069  0 13:08 pts/3    00:00:00 grep python sleep.py
> pgrep 'python sleep.py'
> 

Additional difficulties in identifying a running python script by its name:

  • the actual python string may differ, depending on how the script is executed, for example it may look like this:

/usr/bin/python2.7 sleep.py

  • using the os.path.abspath(__file__) method to locate the script in the process name may fail if the script is a symlink to the actual file and can be executed from both locations

Example:

> cat abs_path.py
import os
print os.path.abspath(__file__)
> ls -l abs_path.py 
lrwxrwxrwx 1 userid at 15 Apr 22 13:22 abs_path.py -> bin/abs_path.py
> python abs_path.py
/home/userid/abs_path.py
> python bin/abs_path.py
/home/userid/bin/abs_path.py
  • processes (including those from python scripts) can actually change their name during execution (maybe not in your script's case, true)

My personal preference in approaching such problem is to have the script create a pidfile in a unique, well known location in which it'll place its own pid. This makes determining the pid of a potentially already running process much more reliable.

You'll still have to take into account the race condition in creating the pidfile and reliably writing into it self's pid. Typically re-checking the file content and committing suicide if mismatches are detected is sufficient to ensure at most a single running process instance exists, regardless of how the process is actually named.

Solution 3

Starting from Dan's answer I came up with

> ps -f -C python | grep 'sleep.py'
userid  21107  2069  0 12:51 pts/3    00:00:00 anypath/python anypath/sleep.py

This still has a lot of the problems described in the previous answers, however, it at least gets rid of seeing grep in the result and being dependent on the path of the script.

It is also possible to check for several python versions with:

> ps -f -C python,python2.7 | grep 'sleepy.py'

EDIT: This is not working correctly if multiple python3 instances are running. Better use:

> pgrep -a python | grep 'sleepy.py'

Solution 4

This question is very similar to Check to see if python script is running

I suggest you use the PidFile module, it will handle everything for you.

import pidfile
import time

print('Starting process')
try:
    with pidfile.PIDFile():
        print('Process started')
        time.sleep(30)
except pidfile.AlreadyRunningError:
    print('Already running.')

print('Exiting')

Try to run twice this script with two terminals

I am not related to the owner of the library, I just found it

Share:
26,806
Martin
Author by

Martin

Updated on July 12, 2022

Comments

  • Martin
    Martin almost 2 years

    i would like my Python script to check, if it is already running and if yes, kill the old process. i tried this, but it doesn't really work...

    import os
    import subprocess
    import signal
    
    process_name = "python " + os.path.abspath(__file__)
    proc = subprocess.Popen(["pgrep", process_name], stdout=subprocess.PIPE)
    
    # Kill process.
    for pid in proc.stdout:
        os.kill(int(pid), signal.SIGTERM)
        # Check if the process that we killed is alive.
        try:
            os.kill(int(pid), 0)
            raise Exception("""wasn't able to kill the process
                              HINT:use signal.SIGKILL or signal.SIGABORT""")
        except OSError as ex:
            continue
    

    It doesn't kill the old process and runs multiple times now.

    • Chris
      Chris about 8 years
      What problem are you trying to solve by doing this? I wonder if there might be a better approach, e.g. creating an init script for your program and letting the init system take care of starting / stopping / restarting it. See meta.stackexchange.com/questions/66377/what-is-the-xy-proble‌​m.
    • Martin
      Martin about 8 years
      The script runs on a Raspberry Pi. It controls an LED Strip. I run it by the command: sudo /opt/fhem/python/start.py & But if it runs multiple times, the signal to the LED strip is sent multiple times too and that leads to flickering lights. That's why i'm looking for a method to kill the old process before starting a new one.
    • Cyb3rFly3r
      Cyb3rFly3r about 8 years
      Are you able to kill the Python script manually (i.e. at the shell)?
    • Dan Cornilescu
      Dan Cornilescu about 8 years
      do you see it actually finding a previous incarnation?
    • Martin
      Martin about 8 years
      I can see the previous incarnations and i can kill them using sudo kill 1234(1234 is the pid)
    • Dan Cornilescu
      Dan Cornilescu about 8 years
      I mean your script seeing them, more specifically are there any pid in proc.stdout?
  • Martin
    Martin about 8 years
    Thank you for that answear, Dan. I have a new idea. I think i will let the script write its pid to a specified textfile on startup. So i can make sure killing the last instance (by the pid given from the textfile) before starting to control the LED's. simplified: read textfile and get old pid -> kill old process -> write new pid to textfile -> start LED control
  • Dan Cornilescu
    Dan Cornilescu about 8 years
    Careful with using a specified filename - if a different filename is used at a subsequent invocation then a previously running process won't be detected.
  • Tahlor
    Tahlor about 3 years
    The only answer that uses pgrep correctly! For me, using this exact command is tricky if you want to determine if the script with this code is already running; if 1 instance is already running and I start another, this code returns at least 3 PIDs for me: one for the original script, one for this second instance, and one for the popen command. Using cmd = [f'pgrep', '-af', f'python.*{process_name}'] and shell=False seems to resolve it for me.
  • FormerAtariUser
    FormerAtariUser over 2 years
    I couldn't get it to work on python 3.7.12 on OSX 11.x ...