Get pid of command ran by screen

删除回忆录丶 提交于 2019-12-08 04:46:39

问题


I'm developing a Minecraft (Bukkit) server manager. It's porpuse is only to start it, stop it, back it up and check if it is running. The last one causes the problem. If I do like this to get the pid of the server:

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

I get the pid of the screen command, not the startup-script. However it seems like the pid is always one underneath the pid of the startup-script, but I suppose this isn't relaiable. How can I get the pid of the java process?

EDIT: I tried this, but ps returns with exit code 1 and no child pid. I think this is because screen closes down imidiately.

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

回答1:


If you have the process ID of the screen (the parent process, which you can access with p.pid assuming you used p = Subprocess.Popen(...)), you can get the child process ids through something like

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

There's also psutil.Process(<SCREEN_PID>).get_children() available from the psutil module which might be preferred to parsing the output of ps since (I think) it parses /proc directly.

There are also some functions inside Python's standard os module that allow you to do some stuff with process IDs directly, but nothing that will get the child process IDs of a parent process id or a process group id.


The following code:

#!/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)

Produces the output:

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

You can access the child pid from the children pid list with cpids[0].

The script simply spawns the screen process with a specific name, then finds the parent process id and from that, the children process ids.

The random characters appended to the screen name are there in case you're running multiple instances using the same script. If you're not, you can remove all that, but it makes no difference leaving it.

The method of finding the parent process id (parsing the output of screen -ls) probably isn't the best, you might alternatively iterate through the processes using psutils.process_iter(). But this seems to work.




回答2:


Assuming that you want the PID of the process running in screen, I answered that in another question on this site. Here is the contents of that answer:

You can get the PID of the screen sessions here like so:

$ 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.

Let us suppose that you want the PID of the program running in Bash in the foo_Monitor screen session. Use the PID of the foo_Monitor screen session to get the PID of the bash session running in it by searching PPIDs (Parent PID) for the known 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

Now get just the PID of the bash session:

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

Now we want the process with that PID. Just nest the commands, and this time use the -v flag on grep bash to get the process that is not bash:

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

Just replace 1814 with the real PID or your screen session:

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



回答3:


You have to wrap your java command into a shell script that obtains and returns the pid. See http://ss64.org/viewtopic.php?id=1495 for an example.



来源:https://stackoverflow.com/questions/15840738/get-pid-of-command-ran-by-screen

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!