获取按屏幕运行的命令pid

时间:2013-04-05 18:14:01

标签: python python-3.x gnu-screen

我正在开发一个Minecraft(Bukkit)服务器管理器。它的porpuse只是启动它,停止它,备份它并检查它是否正在运行。最后一个导致问题。 如果我喜欢这样来获取服务器的pid:

subprocess.Popen(["screen", "-dmS", "minecraft-server", "serverstart.sh"])

我得到了screen命令的pid,而不是startup-script。然而,似乎pid总是在启动脚本的pid下面,但我想这是不可接受的。 我怎样才能获得java进程的pid?

编辑: 我试过这个,但ps返回退出代码1,没有子pid。我认为这是因为屏幕会立即关闭。

check_output(['ps', '--ppid', str(Popen(['screen', '-dmS', 'test']).pid), '--no-headers', '-o', 'pid'])

3 个答案:

答案 0 :(得分:4)

如果您有屏幕的进程ID(父进程,您可以使用p.pid访问,假设您使用p = Subprocess.Popen(...)),则可以通过类似

ps --ppid <SCREEN_PID> --no-headers -o pid

psutil模块中还有psutil.Process(<SCREEN_PID>).get_children(),它可能比解析ps的输出更受欢迎,因为(我认为)它直接解析/proc

Python的standard os module中还有一些函数可以让你直接用进程ID做一些事情,但是什么都不能获得父进程id或进程组id的子进程ID。


以下代码:

#!/bin/env python

import subprocess, random, string, re
import psutil

SERVER_SCRIPT = "./serverstart.sh"

def get_random_key(strlen):
    return 'K'+''.join(random.choice(string.hexdigits) for x in range(strlen-1))

def find_screen_pid(name):
    ph = subprocess.Popen(["screen", "-ls"], stdout=subprocess.PIPE)
    (stdout,stderr) = ph.communicate()
    matches = re.search(r'(\d+).%s' % name, stdout, re.MULTILINE)
    if(matches): 
        pids = matches.groups()
        if(len(pids) == 1): return int(pids[0])
        else: raise Exception("Multiple matching PIDs found: %s" % pids)
    raise Exception("No matching PIDs found")

def get_child_pids(parent_pid):
    pp = psutil.Process(parent_pid)
    return [ int(cp.pid) for cp in pp.get_children()]

# Generate a random screen name, in case you're running multiple server instances
screenname = "minecraft-server-" + get_random_key(5)
print("Creating screen session named: %s" % screenname)
subprocess.Popen(["screen", "-dmS", screenname, SERVER_SCRIPT]).wait()

spid = find_screen_pid(screenname)  # Display some output
print("Screen PID: %d" % spid)
cpids = get_child_pids(spid)
print("Child PIDs: %s" % cpids)

产生输出:

./screen-pid.py
Creating screen session named: minecraft-server-K77d1
Screen PID: 2274
Child PIDs: [2276]

您可以使用cpids[0]从儿童pid列表访问子pid。

该脚本只是使用特定名称生成屏幕进程,然后查找父进程ID,并从中查找子进程ID。

如果您使用相同的脚本运行多个实例,则会附加到屏幕名称的随机字符。如果你不是,你可以删除所有这些,但它没有任何区别。

查找父进程id(解析screen -ls的输出)的方法可能不是最好的,您也可以使用psutils.process_iter()迭代进程。但这似乎有效。

答案 1 :(得分:2)

假设您希望进程的PID在屏幕上运行,我回答了in another question on this site。以下是答案的内容:

您可以在此处获取屏幕会话的PID:

$ screen -ls
There are screens on:
        1934.foo_Server         (01/25/15 15:26:01)     (Detached)
        1876.foo_Webserver      (01/25/15 15:25:37)     (Detached)
        1814.foo_Monitor        (01/25/15 15:25:13)     (Detached)
3 Sockets in /var/run/screen/S-ubuntu.

让我们假设您希望在foo_Monitor屏幕会话中以Bash运行程序的PID。使用foo_Monitor屏幕会话的PID通过在PPID(父PID)中搜索已知的PID来获取其中运行的bash会话的PID:

$ ps -el | grep 1814 | grep bash
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 S  1000  1815  1814  0  80   0 -  5520 wait   pts/1    00:00:00 bash

现在只获得bash会话的PID:

$ ps -el | grep 1814 | grep bash | awk '{print $4}'
1815

现在我们想要那个 PID的过程。只需嵌套命令,这次使用-v上的grep bash标志来获取 bash的进程:

echo $(ps -el | grep $(ps -el | grep 1814 | grep bash | awk '{print $4}') | grep -v bash | awk '{print $4}')
23869

只需用实际PID或屏幕会话替换1814:

echo $(ps -el | grep $(ps -el | grep SCREEN_SESSION_PID | grep bash | awk '{print $4}') | grep -v bash | awk '{print $4}')

答案 2 :(得分:1)

您必须将java命令包装到获取并返回pid的shell脚本中。有关示例,请参阅http://ss64.org/viewtopic.php?id=1495