如何使用独立的stdout,stderr和stdin来分叉新进程?

时间:2015-01-18 10:11:32

标签: python linux subprocess fork

我已经阅读了大部分关于subprocess和os.fork()的相关问题,包括关于双重分叉技巧的所有讨论。但是,这些解决方案似乎都不适合我的方案。

我想分叉一个新进程并允许父进程终止(通常)而不会搞砸孩子的stdin,stdout和stderr而不会杀死孩子。

我的第一次尝试是使用subprocess.Popen()

#!/usr/bin/python

from subprocess import call,Popen

Popen("/bin/bash", shell=True)
call("echo Hello > /tmp/FooBar", shell=True)

这会失败,因为父进程退出时会导致子进程被终止。我知道creationflags但这是特定于Windows的,我在Linux上运行。请注意,如果我们通过在其末尾添加无限循环来简单地保持父进程处于活动状态,则上述代码可以很好地工作。这是不可取的,因为父母已经完成了它的工作,并没有真正的理由让它坚持下去。


第二次尝试是使用os.fork()

#!/usr/bin/python

from subprocess import call
from os import fork


try: 
  pid = fork()
  if pid > 0: 
    pass    
  else: # child process will start interactive process
    call("/bin/bash", shell=True)
except:
  print "Forking failed!"

call("echo Hello > /tmp/FooBar", shell=True)

这里,子进程不再与父进程一起死亡,但在父进程死后,子进程无法再读取输入和写入输出。

因此,我想知道如何使用完全独立的stdoutstderrstdin来分叉新流程。独立意味着父进程可以终止(通常),并且子进程(无论是bash还是tmux还是任何其他交互程序)的行为就像父程序没有终止一样。更准确地说,请考虑原始程序的以下变体。

#!/usr/bin/python

from subprocess import call,Popen

Popen("/bin/bash", shell=True)
call("echo Hello > /tmp/FooBar", shell=True)
while True:
   pass

上面的代码具有我所寻求的所有行为,但它使Python过程保持人为的活动。我试图实现这个确切的行为,没有Python进程活着

警告:我正在通过ssh运行这些应用程序,因此产生新的GUI窗口不是一个可行的解决方案。

所需行为

  1. 我运行python代码。
  2. 我得到了一个闪亮的新bash shell,其工作方式与我开始使用的bash shell完全相同。
  3. 创建了文件/tmp/FooBar
  4. 原始Python脚本完成。
  5. 我继续使用我的闪亮的新bash shell,ps aux | grep python的输出不包含我刚刚运行的python脚本。

2 个答案:

答案 0 :(得分:0)

你的第一个例子对Popen()有一个额外的不必要的调用,因为call便利函数只会在shell中执行它的命令并退出,所以如果你刚刚运行它会起作用:

from subprocess import call, Popen

call("echo Hello > /tmp/FooBar", shell=True)

但是如果你想将一系列命令发送到shell,那么shell需要打开,stdin连接到管道,这样它就不会与父stdin混在一起(这实际上是你要求的)获得独立标准的条款):

p = Popen("/bin/bash", shell=True, stdin = subprocess.PIPE)
p.stdin.write("echo Hello > /tmp/FooBar\n")
p.stdin.write("date >> /tmp/FooBar\n")
p.terminate()

如果你还要控制输出,那么你将stdout和stderr重定向到PIPE(即stdout = subprocess.PIPEstderr = subprocess.PIPE),然后调用p.stdout.read()以获得所需的输出。 / p>

要允许进程在退出python后继续运行,可以在命令末尾添加&运算符,以便它继续在后台运行,例如:

call("nc -l 2000 > /tmp/nc < &",shell=True)

要让进程运行,以便stdin和stdout以及仍然连接的进程可以使用shell重定向。要保持对stdin的访问,可以使用mkfifo创建命名管道,例如:

call("mkfifo /tmp/pipe",shell=True)
call("tail -f /tmp/pipe > /tmp/out &",shell=True)

要提供输入stdin,只需将数据发送到管道即.e.g。来自shell:

$ echo 'test' > /tmp/pipe

答案 1 :(得分:0)

我最近遇到了这个问题,我找到了可能会有所帮助的解决方案, 使用creationflags告诉Popen,子进程不应继承父进程控制台,因此拥有自己的stdout,stdin和stderr,就好像它是父进程一样。

subprocess.Popen("/bin/bash", creationflags= subprocess.CREATE_NEW_CONSOLE)

Popen根据文档支持creationflags关键字参数:

  

creationflags(如果提供)可以是以下一个或多个标志:

     

CREATE_NEW_CONSOLE

     

CREATE_NEW_PROCESS_GROUP

     

ABOVE_NORMAL_PRIORITY_CLASS

     

BELOW_NORMAL_PRIORITY_CLASS

     

HIGH_PRIORITY_CLASS

     

IDLE_PRIORITY_CLASS

     

NORMAL_PRIORITY_CLASS

     

REALTIME_PRIORITY_CLASS

     

CREATE_NO_WINDOW

     

DETACHED_PROCESS

     

CREATE_DEFAULT_ERROR_MODE

     

CREATE_BREAKAWAY_FROM_JOB

您感兴趣的是DETACHED_PROCESSCREATE_NEW_CONSOLE,我用过CREATE_NEW_CONSOLE,它的工作是产生一个新的过程,就像它是父进程一样,我使用了Python3.5 Windows,在Python 3.7中添加了DETACHED_PROCESS,并记录了它与CREATE_NEW_CONSOLE相同。

相关问题