2

I want to know how I can stop my current python script when it is already running.

To be clear, I want to write it in my own python script. So the very first thing, in main method, it will check if my python script is already running or not. If it is running, I want to exit out with an error message.

Currently I am trying this code:

if os.system("ps ax | grep sample_python.py") == True:
     print "fail"
     exit(0)
else:
     print "pass"

The above code looks like it is grepping the python name.. but it always go to else loop instead of going into the if loop and exit..

So for testing purposes, I run my script and on another terminal I run my script again. First script doesn't stop but 2nd python doesn't go into the if loop to print out error message.

How can I fix this?


Okay I found a dumb thing and I want to see if someone can help me go around it.. So my point was to kill the python that is just started if another python is currently running.. but the dumb thing is.. since I am running my python and running a checking coding inside of this python.. it will always exit.. because as soon as I start the python PID will be created and will be running...

So.. python starts -> pid created and running -> check if python is running -> yes python is running -> exit.

I want to see if there is a way to do python starts -> check if python is already running or not -> if it is running kill the current python not the one that was running.

Tim
  • 103
  • 2
  • 9
  • 1
    grep will find itself if it finds no match – Padraic Cunningham Mar 30 '15 at 19:58
  • The way to diagnose the problem you present is to first type in the command line `ps ax ...` both when your script is running and when not to check if you have the right command line. Then you write a python script which just prints the result of the `os.system( ...` call, again in both cases to check that there is a difference. Then you should be able to write the script you need. – quamrana Mar 30 '15 at 20:03
  • You can use `pgrep` and `pkill` to find and kill processes without including the grep process itself (there are also various shell utilities to ensure only one copy of something is running, though they might not be appropriate for your situation) – DNA Mar 30 '15 at 20:26
  • I think it might also be failing because when I do a `ps | grep progname`, one of the processes that gets listed is grep command. And grep's command line parameter "progname" is included. This might be OS dependent. – MACE Aug 16 '20 at 13:45

6 Answers6

7

A lockfile with PID of the running process is a more systematic way how to this. Program just checks at the start whether a PID file both exists and is locked (flock or lockf); if so, it means that another instance of this program is still running. If not, it creates (or rewrites) the PID file and locks it.

The flock/lockf lock has the advantage that operating system automatically removes the lock in case of program termination.

See here how to do this in Python:

Python: module for creating PID-based lockfile?

Community
  • 1
  • 1
Messa
  • 24,321
  • 6
  • 68
  • 92
  • Just FYI, the script linked in the accepted answer has been updated since the answer was written and has a greater dependency on the Mercurial package than it originally did. This might not be a problem for the OP though. – detly Mar 30 '15 at 20:31
  • Yes - the PID is the go-to standard for this. See also http://stackoverflow.com/questions/688343/reference-for-proper-handling-of-pid-file-on-unix – Jonathan Mar 30 '15 at 20:32
1

Instead of using grep, why not create a lock file? One instance of your program creates a file in a known location, and deletes it upon exit. Any other instance has to check for the existence of that file, and should only continue running if it already exists.

The os.open() function has specific flags for this:

import os

LOCKFILE_LOCATION = "/path/to/lockfile"

try:
    os.open(LOCKFILE_LOCATION, os.O_CREAT|os.O_WRONLY|os.O_EXCL)
    # Maybe even write the script's PID to this file, just in case you need
    # to check whether the program died unexpectedly.
except OSError:
    # File exists or could not be created. Can check errno if you really
    # need to know, but this may be OS dependent.
    print("Failed to create lockfile. Is another instance already running?")
    exit(1)
else:
    print("Pass")
    # Run the rest of the program.
    # Delete the file
    os.remove(LOCKFILE_LOCATION)

Try this out by putting, say, import time; time.sleep(20) after `print("Pass") and running multiple instances of this script; all but one should fail.

detly
  • 29,332
  • 18
  • 93
  • 152
0

os.system() returns the exit value of the command that was run. If the ps was successful, it returns 0, which is not equivalent to True. You could explicitly test if the command returned zero, which should bring you into the if block.

Edit: To do this needs os.subprocess. These four commands should get what you want.

p1 = subprocess.Popen(['ps', 'ax'], stdout=subprocess.PIPE)
p2 = subprocess.Popen(['grep', 'bash'], stdin=p1.stdout, stdout=subprocess.PIPE)
p3 = subprocess.Popen(['wc', '-l'], stdin=p2.stdout, stdout=subprocess.PIPE)
count = int(p3.stdout.read())
if count > 1:
    print('yes')

Replace "bash" with the name of your script. What this does is get the list of running processes, pipes that output to grep to winnow out all but your script (or bash instances in the code sample), pipes that output to wc to get a count of lines, and then asks that process for its stdout which is the count of the instances of the script that are currently running. You can then use that count variable in an if statement, where if there is more than one instance running, you could abort the run.

  • thank you but I got a problem now.. when I run my coding `if os.system("ps ax | grep sample_python.py")`.. it always prints ` 5440 pts/0 S+ 0:00 sh -c ps ax | grep sample_python.py` and `5442 pts/0 S+ 0:00 grep sample_python.py` and exits now.. even if my python is not running or just started.. ` – Tim Mar 30 '15 at 20:03
  • If both scripts have the same name, as it sounds like, then ps/grep will always return at least one row. So you could run the output of the grep through wc to see if there were two or more rows. An alternative approach would be to create a lock file that gets deleted at the end of the run of the script - then you could test whether it exists, and if not create it and run the script. – devinformatics Mar 30 '15 at 20:07
  • I don't understand.. can you explain little more? – Tim Mar 30 '15 at 20:08
  • If I understand the question, you want to start a script and its first action is to detect if another copy of the same script is running. The system call will always find at least the current script itself. But if another copy is running it should detect two processes. wc -l does a count of lines, so you could test if there are two or more lines. Is that the part about which you were asking? – devinformatics Mar 30 '15 at 20:17
  • yes this is exactly what I wanted to do.. but now.. how can I change my coding to do this? – Tim Mar 30 '15 at 20:48
  • I realized I was just recreating the same problem. Can be solved using os.subprocess. I will edit the answer. – devinformatics Mar 30 '15 at 21:04
  • I think the answer from Alexey V. Paramonov is simpler, though you will always get the current script listed in the output. Or detly's response about the lock file may actually be simplest. – devinformatics Mar 30 '15 at 21:16
0

More pythonic way:

import os
import psutil

script_name = os.path.basename(__file__)
if script_name in [p.name() for p in psutil.get_process_list()]:
   print "Running"
Alex Paramonov
  • 2,630
  • 2
  • 23
  • 27
  • 1
    `psutil.get_process_list()` was deprecated in 2012. [source](https://github.com/giampaolo/psutil/blob/73337e03e8eb6f78dba791a91da34cd244172159/HISTORY.rst#050) the pythonic way now is to use iterables. – Wis Mar 29 '19 at 08:51
0

you can use the cross-platform library psutil to iterate over processes, parse the commanline of each process and check if it's a python process with the same script path, then you can either stop the execution when you find only one or kill/stop the old instance/s and continue with the new one.

import os
import psutil
import sys

for proc in psutil.process_iter():
    if "python" in proc.name():
        if len(proc.cmdline()) > 1:
            script_path = sys.argv[0]
            proc_script_path = proc.cmdline()[1]
            if script_path.startswith("." + os.sep) or script_path.startswith(".." + os.sep):
                script_path = os.path.normpath(os.path.join(os.getcwd(), script_path))
            if proc_script_path.startswith("." + os.sep) or proc_script_path.startswith(".." + os.sep):
                proc_script_path = os.path.normpath(os.path.join(proc.cwd(), proc_script_path))
            if  script_path == proc_script_path and os.getpid() != proc.pid:
                #proc.kill() # you can terminate all the other instances
                sys.exit() # or your way, terminate this newer instance here
Wis
  • 484
  • 7
  • 22
-1

Because os.system("ps ax | grep sample_python.py") return 0. It is not true.

Look this question: Assign output of os.system to a variable and prevent it from being displayed on the screen

You must write

os.popen('cat /etc/services').read() 

But such way you will always go to the "failed" because your script already started. You'll must count number of returned lines...

Look here as a variant of single instance:

Python: single instance of program

I wrote such script. I created a file with any name on start, and deleted at the end. It was the daemon script. On start I checked if that file exists.

Not bad variant is here Ensure a single instance of an application in Linux:

import fcntl
pid_file = 'program.pid'
fp = open(pid_file, 'w')
try:
    fcntl.lockf(fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
    # another instance is running
    sys.exit(1)
Community
  • 1
  • 1
Elena NNN
  • 217
  • 1
  • 6