检查Python脚本是否已在运行

10

我希望我的Python脚本能够检查是否已经在运行,如果是,则终止旧进程。我尝试过这个方法,但它并没有真正起作用...

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

它不会终止旧的进程,而是现在可以运行多次。


4
你试图通过这样做解决什么问题?我想知道是否有更好的方法,比如为你的程序创建一个初始化脚本,让初始化系统负责启动/停止/重启它。请参阅https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem. - Chris
该脚本在树莓派上运行,控制着一条LED灯带。我通过命令运行它:sudo /opt/fhem/python/start.py & 但如果它运行多次,信号也会被发送多次,导致灯光闪烁。这就是为什么我正在寻找一种在启动新进程之前终止旧进程的方法。 - Martin
你能手动终止 Python 脚本吗(例如在 shell 中)? - Cyb3rFly3r
你认为它能够找到先前的版本吗? - Dan Cornilescu
我可以看到之前的进程,并且可以使用 sudo kill 1234(1234 是 pid) 来杀死它们。 - Martin
我的意思是,你的脚本是否看到了它们,更具体地说,proc.stdout 中是否有任何 pid - Dan Cornilescu
5个回答

9

我的当前解决方案在OSX上看起来像这样。我正在使用pgrep与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")

据我测试,即使Python字符串不同,这也应该有效。脚本的位置也无关紧要。

唯一正确使用 pgrep 的答案!对我来说,如果您想确定是否已经运行了具有此代码的脚本,则使用此确切命令很棘手;如果已经运行了1个实例并且我启动另一个,则此代码对我返回至少3个PID:原始脚本的一个,第二个实例的一个以及popen命令的一个。对于我来说,使用 cmd = [f'pgrep', '-af', f'python.*{process_name}']shell=False 似乎可以解决这个问题。 - Tahlor

5

使用基于pgrep的方法来确定Python脚本是否正在运行是不可靠的。例如:

> 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'
> 

识别正在运行的Python脚本名称的其他困难之处:

  • 实际的python字符串可能会有所不同,这取决于脚本的执行方式,例如它可能看起来像这样:

/usr/bin/python2.7 sleep.py

  • 如果脚本是指向实际文件的符号链接,并且可以从两个位置执行,则使用os.path.abspath(__file__)方法在进程名称中定位脚本可能会失败。

示例:

> 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
  • 进程(包括Python脚本中的进程)在执行过程中实际上可以更改它们的名称(也许在您的脚本中不是这种情况,是真的)

我个人处理这类问题的首选方法是让脚本在一个独特且众所周知的位置创建一个pidfile,并将自己的pid放入其中。这样可以更可靠地确定潜在已运行进程的pid。

您仍然需要考虑创建pidfile时的竞争条件以及可靠地写入其自身pid。通常,重新检查文件内容并在检测到不匹配时自杀足以确保最多只存在一个运行的进程实例,而不管该进程的实际名称如何。


谢谢你的回答,丹。我有一个新想法。我想让脚本在启动时将其pid写入指定的文本文件中。这样我就可以确保在开始控制LED之前杀死上一个实例(通过从文本文件中获取的pid)。简化:读取文本文件并获取旧pid -> 杀死旧进程 -> 将新pid写入文本文件 -> 开始LED控制 - Martin
1
小心使用指定的文件名 - 如果在随后的调用中使用了不同的文件名,那么先前正在运行的进程将无法被检测到。 - Dan Cornilescu

4

Dan的答案开始,我想到了以下内容:

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

这个方法仍然存在前面答案中描述的很多问题,但至少消除了在结果中看到grep和依赖脚本路径的问题。

同时,也可以使用以下命令检查几个Python版本:

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

编辑:如果有多个python3实例正在运行,则此方法不起作用。更好的方法是使用:

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

3

如果你想要一个不使用库的跨平台解决方案,你可以使用PID文件。PID文件是一个简单的TXT文件,里面包含了你的进程标识符(PID)。

from os import getpid
from os.path import exists
from psutil import pid_exists, Process

PATH_PIDFILE = 'pid.txt'

def already_running():
    my_pid = getpid()
    if exists(PATH_PIDFILE):
        with open(PATH_PIDFILE) as f:
            pid = f.read()
            pid = int(pid) if pid.isnumeric() else None
        if pid is not None and pid_exists(pid) and Process(pid).cmdline() == Process(my_pid).cmdline():
            return True
    with open(PATH_PIDFILE, 'w') as f:
        f.write(str(my_pid))
    return False

3

这个问题非常类似于检查Python脚本是否正在运行

我建议您使用PidFile模块,它将为您处理一切。

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')

请尝试在两个终端中运行此脚本两次

我与该库的所有者无关,我只是找到它了


我无法在OSX 11.x上的Python 3.7.12上使其工作... - FormerAtariUser

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接