【问题标题】:How can I start a process and put it to background in python?如何在 python 中启动一个进程并将其置于后台?
【发布时间】:2016-02-21 09:08:23
【问题描述】:

我目前正在编写我的第一个 python 程序(在 Python 2.6.6 中)。该程序有助于启动和停止在服务器上运行的不同应用程序,并提供用户常用命令(例如在 Linux 服务器上启动和停止系统服务)。

我正在通过

启动应用程序的启动脚本
p = subprocess.Popen(startCommand, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate()
print(output)

问题是,一个应用程序的启动脚本停留在前台,因此 p.communicate() 永远等待。我已经尝试在 startCommand 前面使用“nohup startCommand &”,但这并没有按预期工作。

作为一种解决方法,我现在使用以下 bash 脚本来调用应用程序的启动脚本:

#!/bin/bash

LOGFILE="/opt/scripts/bin/logs/SomeServerApplicationStart.log"

nohup /opt/someDir/startSomeServerApplication.sh >${LOGFILE} 2>&1 &

STARTUPOK=$(tail -1 ${LOGFILE} | grep "Server started in RUNNING mode" | wc -l)
COUNTER=0

while [ $STARTUPOK -ne 1 ] && [ $COUNTER -lt 100 ]; do
   STARTUPOK=$(tail -1 logs/SomeServerApplicationStart.log | grep "Server started in RUNNING mode" | wc -l)
   if (( STARTUPOK )); then
      echo "STARTUP OK"
      exit 0
   fi
   sleep 1
   COUNTER=$(( $COUNTER + 1 ))
done

echo "STARTUP FAILED"

bash 脚本是从我的 python 代码中调用的。这种解决方法很完美,但我更愿意在 python 中做所有事情......

是 subprocess.Popen 方式错误吗?我怎么能只用 Python 完成我的任务?

【问题讨论】:

  • 在需要的时候使用communicate就行了..你不需要检查结果吗?只是避免它...
  • @klashxx:我确实需要结果。那是我的问题...(需要检查“服务器以 RUNNING 模式启动”的输出...)
  • @daTokenizer:所以解决方案是避免 subprocess.Popen 并使用 sytem() 或 os.spawn 并分别检查输出?

标签: python linux bash subprocess daemon


【解决方案1】:

首先,在通信中不阻塞 Python 脚本很容易......通过不调用通信!只需从命令的输出或错误输出中读取,直到找到正确的消息并忘记该命令。

# to avoid waiting for an EOF on a pipe ...
def getlines(fd):
    line = bytearray()
    c = None
    while True:
        c = fd.read(1)
        if c is None:
            return
        line += c
        if c == '\n':
            yield str(line)
            del line[:]

p = subprocess.Popen(startCommand, shell=True, stdout=subprocess.PIPE,
               stderr=subprocess.STDOUT) # send stderr to stdout, same as 2>&1 for bash
for line in getlines(p.stdout):
    if "Server started in RUNNING mode" in line:
        print("STARTUP OK")
        break
else:    # end of input without getting startup message
     print("STARTUP FAILED")
     p.poll()    # get status from child to avoid a zombie
     # other error processing

上面的问题是,服务器仍然是 Python 进程的子进程,可能会收到不需要的信号,例如 SIGHUP。如果你想让它成为一个守护进程,你必须首先启动一个子进程来启动你的服务器。这样,当第一个孩子结束时,调用者可以等待它,服务器将获得 1 的 PPID(由 init 进程采用)。您可以使用多处理模块来简化该部分

代码可能是这样的:

import multiprocessing
import subprocess

# to avoid waiting for an EOF on a pipe ...
def getlines(fd):
    line = bytearray()
    c = None
    while True:
        c = fd.read(1)
        if c is None:
            return
        line += c
        if c == '\n':
            yield str(line)
            del line[:]

def start_child(cmd):
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                         shell=True)
    for line in getlines(p.stdout):
        print line
        if "Server started in RUNNING mode" in line:
            print "STARTUP OK"
            break
    else:
        print "STARTUP FAILED"

def main():
    # other stuff in program
    p = multiprocessing.Process(target = start_child, args = (server_program,))
    p.start()
    p.join()
    print "DONE"
    # other stuff in program

# protect program startup for multiprocessing module
if __name__ == '__main__':
    main()

当一个文件对象本身是一个一次返回一行的迭代器时,人们可能想知道getlines 生成器的需要是什么。问题是它在内部调用read,当文件未连接到终端时读取直到EOF。由于它现在已连接到 PIPE,因此在服务器结束之前您将一无所获……这不是预期的

【讨论】:

  • 完美!非常感谢您的回答。现在我确切地知道该怎么做了!
  • 这篇文章对我面临的类似挑战非常有帮助。但是,为了使它工作,我必须将“if c == '\n'”更改为“if c == b'\n'”
猜你喜欢
  • 2018-09-04
  • 1970-01-01
  • 1970-01-01
  • 2014-05-04
  • 2015-09-14
  • 2011-01-23
  • 1970-01-01
  • 1970-01-01
  • 2016-04-15
相关资源
最近更新 更多