Python 子进程挂起命名管道
Posted
技术标签:
【中文标题】Python 子进程挂起命名管道【英文标题】:Python subprocess hangs with named pipes 【发布时间】:2013-11-08 12:37:06 【问题描述】:我苦于试图模仿bash
这个简单的片段:
$ cat /tmp/fifo.tub &
[1] 24027
$ gunzip -c /tmp/filedat.dat.gz > /tmp/fifo.tub
line 01
line 02
line 03
line 04
line 05
line 06
line 07
line 08
line 09
line 10
[1]+ Done cat /tmp/fifo.tub
基本上我尝试了这种subprocess
方法:
# -*- coding: utf-8 -*-
import os
import sys
import shlex
import pprint
import subprocess
def main():
fifo = '/tmp/fifo.tub'
filedat = '/tmp/filedat.dat.gz '
os.mkfifo(fifo,0777)
cat = "cat %s" % fifo
args_cat = shlex.split(cat)
pprint.pprint(args_cat)
cat = subprocess.Popen( args_cat,
close_fds=True,
preexec_fn=os.setsid)
print "PID cat: %s" % cat.pid
f = os.open(fifo ,os.O_WRONLY)
gunzip = 'gunzip -c %s' % (filedat)
args_gunzip = shlex.split(gunzip)
pprint.pprint(args_gunzip)
gunzip = subprocess.Popen( args_gunzip,
stdout = f,
close_fds=True,
preexec_fn=os.setsid)
print "PID gunzip: %s" % gunzip.pid
while not cat.poll():
# hangs for ever
pass
return True
if __name__=="__main__":
main()
cat
进程永远结束。
另外,我尝试用 threads 绕过问题,但得到了相同的结果。
import os
import sys
import shlex
import pprint
import subprocess
import threading
class Th(threading.Thread):
def __init__(self,cmd,stdout_h=None):
self.stdout = None
self.stderr = None
self.cmd = cmd
self.stdout_h = stdout_h
self.proceso = None
self.pid = None
threading.Thread.__init__(self)
def run(self):
if self.stdout_h:
self.proceso = subprocess.Popen(self.cmd,
shell=False,
close_fds=True,
stdout=self.stdout_h)
else:
self.proceso = subprocess.Popen( self.cmd,
close_fds=True,
shell=False)
print "PID: %d" % self.proceso.pid
def main():
fifo = '/tmp/fifo.tub'
filedat = '/tmp/filedat.dat.gz '
try:
os.unlink(fifo)
except:
pass
try:
os.mkfifo(fifo,0777)
except Exception , err:
print "Error '%s' tub %s." % (err,fifo)
sys.exit(5)
cat = "cat %s" % fifo
args_cat = shlex.split(cat)
pprint.pprint(args_cat)
cat = Th(cmd=args_cat)
cat.start()
try:
f = os.open(fifo ,os.O_WRONLY)
except Exception, err:
print "Error '%s' when open fifo %s " % (err,fifo)
sys.exit(5)
gunzip = 'gunzip -c %s ' % (filedat)
args_gunzip = shlex.split(gunzip)
pprint.pprint(args_gunzip)
gunzip = Th(cmd=args_gunzip,stdout_h=f)
gunzip.start()
gunzip.join()
cat.join()
while gunzip.proceso.poll() is None:
pass
if cat.proceso.poll() is None:
print "Why?"
cat.proceso.terminate()
return True
if __name__=="__main__":
main()
我显然遗漏了一些东西,非常欢迎任何帮助。
【问题讨论】:
【参考方案1】:您没有关闭 FIFO 文件描述符,所以 cat 只是挂在那里,以为还有更多内容。
我认为您也可以使用 .wait() 方法来执行与您的 while 循环相同的操作。
# -*- coding: utf-8 -*-
import os
import sys
import shlex
import pprint
import subprocess
def main():
fifo = '/tmp/fifo.tub'
filedat = '/tmp/filedat.dat.gz '
os.mkfifo(fifo,0777)
cat = "cat %s" % fifo
args_cat = shlex.split(cat)
pprint.pprint(args_cat)
cat = subprocess.Popen( args_cat,
close_fds=True,
preexec_fn=os.setsid)
print "PID cat: %s" % cat.pid
f = os.open(fifo ,os.O_WRONLY)
gunzip = 'gunzip -c %s' % (filedat)
args_gunzip = shlex.split(gunzip)
pprint.pprint(args_gunzip)
gunzip = subprocess.Popen( args_gunzip,
stdout = f,
close_fds=True,
preexec_fn=os.setsid)
print "PID gunzip: %s" % gunzip.pid
gunzip.wait()
print "gunzip finished"
os.close(f)
cat.wait()
print "cat finished"
return True
if __name__=="__main__":
main()
【讨论】:
这对你有用吗?我唯一担心的是它在 cat 完成之前关闭文件描述符,但我怀疑它只是为 python 进程关闭它而不是 cat 即使在gunzip
是exec
ed 之前发生重定向,您也可以关闭父管道的写入端。 with
-statement can help with cleaning things up
成功了@Necrolyte2! ,来自 bash 的隐式关闭很容易跳过
我也花了一段时间才找到错误。确保查看@J.F.Sebastian 发布的 GIST 以及一些很棒的语句用法。
@klashxx:在这种情况下,您需要在gunzip
调用之后添加cat.wait()
(在我的代码中,它由with
-statement 自动调用)以上是关于Python 子进程挂起命名管道的主要内容,如果未能解决你的问题,请参考以下文章
尝试将 StreamWriter 打开到命名管道时,Mono 挂起