Python:分叉、管道与执行

8 投票
3 回答
14803 浏览
提问于 2025-04-17 12:40

我想在一个Python应用程序中运行一个程序,它会在后台运行,但最终会显示在前台。

这个程序有一个图形用户界面(GUI)来与之互动。不过,它的控制是通过控制台的标准输入(stdin)和标准输出(stdout)来提供的。我希望能够通过我应用的图形界面来控制它,所以我最初的想法是:

  • 创建一个新进程(Fork)
  • 在父进程中,使用dup2来处理标准输入和标准输出,以便访问它们
  • 执行子进程(exec)

在Python中实现这个想法简单吗?怎么做?有没有其他方法可以实现我想要的效果?那会是什么呢?

3 个回答

3

这个结构其实并不复杂,搭建起来很简单!

看看这个例子

if os.fork():
    os._exit(0)
    os.setsid()
    os.chdir("/")
    fd = os.open("/dev/null", os.O_RDWR)
    os.dup2(fd, 0)
    os.dup2(fd, 1)
    os.dup2(fd, 2)
if fd 2:
    os.close(fd)

这段Python代码设置了一个ID,改变了目录,打开了一个文件,然后处理它并关闭!

6

首先,Python的 subprocess模块 是正确的选择。

下面是一个使用subprocess的例子:

import subprocess
x = subprocess.check_output(["echo","one","two","three"])

这里的x将是输出(Python3中的字节类:x.decode('utf-8') 用于转换成字符串)

需要注意的是,这样做不会复制错误输出(stderr)。如果你也需要错误输出,可以尝试下面的方式:

x = subprocess.check_output(["bash","-c", 'echo foo; echo bar >&2'],stderr=subprocess.STDOUT)

当然,还有很多其他方法可以捕获错误输出,包括将其放到不同的输出变量中。

直接控制的使用

不过,如果你在做一些复杂的事情,需要直接控制,可以看看下面的代码:

import os
rside, wside = os.pipe()
if not os.fork():
    # Child

    os.close(rside)
    # Make stdout go to parent
    os.dup2(wside, 1)
    # Make stderr go to parent
    os.dup2(wside, 2)
    # Optionally make stdin come from nowhere
    devnull = os.open("/dev/null", os.O_RDONLY)
    os.dup2(devnull, 0)
    # Execute the desired program
    os.execve("/bin/bash",["/bin/bash","-c","echo stdout; echo stderr >&2"],os.environ)
    print("Failed to exec program!")
    sys.exit(1)

# Parent
os.close(wside)
pyrside = os.fdopen(rside)

for line in pyrside:
   print("Child (stdout or stderr) said: <%s>"%line)

# Prevent zombies!  Reap the child after exit
pid, status = os.waitpid(-1, 0)
print("Child exited: pid %d returned %d"%(pid,status))

注意: @初学者的回答 在几个方面是有问题的:包含了os._exit(0),这会立即导致子进程退出,使得其他部分变得毫无意义。没有os.execve(),这让问题的主要目标变得无效。也没有办法访问子进程的标准输出/错误输出,这也是另一个问题的目标。

5

使用标准的Python子进程模块,这个操作相对简单:

http://docs.python.org/py3k/library/subprocess.html

撰写回答