如何使用 Python 中的 subprocess 模块启动和停止 Linux 程序?

Posted

技术标签:

【中文标题】如何使用 Python 中的 subprocess 模块启动和停止 Linux 程序?【英文标题】:How do I start and stop a Linux program using the subprocess module in Python? 【发布时间】:2011-07-27 20:30:14 【问题描述】:

我正在编写一个使用 Selenium 对另一个网站进行屏幕抓取的网络应用程序。这种屏幕抓取每天只发生一次,所以我宁愿不要让 Selenium 和 Xvfb 一直运行。

我试图弄清楚如何从 Python 启动 Xvfb 和 Selenium,然后在屏幕抓取完成后停止它们。

如果我手动操作,我会在命令行启动它们,然后按 CTRL C 来停止它们。我正在尝试用 Python 做同样的事情。

我好像可以这样成功启动Xvfb:

xvfb = Popen('Xvfb :99 -nolisten tcp', shell=True)

但是当我试图终止它时:

xvfb.terminate()

然后尝试再次启动它(通过重复我的初始命令),它告诉我它已经在运行。

【问题讨论】:

这获得了 5 票赞成?我以为我真的很愚蠢! xvfb.terminate() 之后尝试xvfb.wait()。如果做不到这一点,请尝试xvfb.kill() Ctrl-C 将 SIGTERM 发送到 Unix 进程。您的用户 python 进程无法将 SIGTERM 发送到以 root 身份运行的 Xvfb。获取您孩子的 pid,执行“sudo kill ”,然后执行“xvfb.wait()”。 @Spike:啊,好吧,这很有道理。也许我至少应该让 Xvfb 一直运行,因为它似乎需要一个 sudoer 来启动它,而且我想这对于 Python 来说会有点尴尬? (到目前为止,我只是在 Python shell 中输入密码,这在正常运行时显然不起作用。) 你解释你想要发生什么,你是如何尝试去做的(带代码),以及结果如何不是你所期望的。这是一个写得很好的问题,值得一票。 【参考方案1】:

我不知道您为什么要以 root 身份运行 Xvfb。您通常的 X 服务器只需要以 root 身份运行(在许多但不是所有的 unice 上),以便它可以访问视频硬件;根据定义,这对 Xvfb 来说不是问题。

tempdir = tempfile.mkdtemp()
xvfb = subprocess.Popen(['Xvfb', ':99', '-nolisten', 'tcp', '-fbdir', tempdir])

当您终止 X 服务器时,您可能会看到 zombie process。这实际上不是一个进程(它已经死了),只是进程表中的一个条目,当父进程读取子进程的退出状态或自身死亡时消失。僵尸大多是无害的,但调用wait 读取退出状态会更干净。

xvfb.terminate()
# At this point, `ps -C Xvfb` may still show a running process
# (because signal delivery is asynchronous) or a zombie.
xvfb.wait()
# Now the child is dead and reaped (assuming it didn't catch SIGTERM).

【讨论】:

@Gilles:啊,明白了——我已经复制并粘贴了我用来从某处网页启动 Xvfb 的命令。我没有意识到这是 fbdir 选项,如果我不以 root 身份运行它,它会导致它无法工作。我现在已经删除了它,并且可以像我的普通用户一样运行它,但是 .terminate().kill() 似乎仍然没有停止 Xvfb。 @Paul:我在Popen 通话中犯了几个错别字,但我认为你没看错。你确定.terminate().kill() 没有终止进程吗?僵尸可能仍然存在(当您的 Python 脚本终止时它会消失),请参阅我的编辑。 @Gilles:是的,看起来这个过程正在徘徊。在.terminate().kill() 和我退出 Python shell 之后,ps -C Xvfb 仍然报告 Xvfb 正在运行。当我尝试再次启动 Xvfb 时,它还说服务器已经处于活动状态。 (我认为这只是因为文件/tmp/.X99-lock 仍然存在,但ps -C Xvfb 确认该进程仍在运行。) @Gilles: AHA — 但是当我按照您建议的方式使用Popen(即使用参数,而不是字符串和shell=True)时,一切正常如你所说。呸!非常感谢您对此进行调查,我缺少一些基本的 Unix/Linux 知识,因此在尝试调试问题时往往会有点难过。愿你的答案获得一千个赞。 @Paul:啊,所以你是在杀死 shell 而不是 Xvfb。如果您需要在调用 Xvfb 之前而不是之后进行 shell 扩展,您可以运行 Popen("exec Xvfb …", shell=True) 以便 Xvfb 进程将替换 shell 而不是作为子进程运行。如果你之后需要 shell,你需要以某种方式将 Xvfb 的进程 ID 传递给 python 代码。但是在这里,不使用 shell 是最简单的。【参考方案2】:

我假设您可以对系统进行参数化,以允许任何用户启动 Xvfb,正如 here 解释的那样,解决您的所有问题

编辑 正确的命令行是

sudo chmod u+s `which Xvfb` 

【讨论】:

可能——其中哪一部分允许任何用户启动 Xvfb?是sudo mod u+s which Xvfb``?我不熟悉mod 命令,而且它对谷歌搜索有点免疫——你能扩展一下吗? 事实上它应该是chmod u+s,正如这里所解释的en.wikipedia.org/wiki/Chmod 我怀疑mod 命令的存在是为了对命令有帮助,只需写man command @Xavier:啊!事实上,这非常适合让非 root 用户运行 Xvfb。不幸的是,.terminate().kill() 似乎仍然没有停止这个过程。 @Paul 你是对的我错过了事实上它会启动一个根程序并识别为根。但是您的程序仍然以 root 身份运行

以上是关于如何使用 Python 中的 subprocess 模块启动和停止 Linux 程序?的主要内容,如果未能解决你的问题,请参考以下文章

python中的subprocess.Popen()使用

为啥不在 Python 中的 subprocess.Popen 中使用 `shell=True`? [复制]

如何使用 python 2.7.6 使 subprocess.call 超时?

如何在 python 中杀死使用 subprocess.call 调用的子进程? [复制]

将python中的双引号shell命令传递给subprocess.Popen()?

python 模块积累-----subprocess