我们最近发现,我们开发的脚本在python 3.x(但不是python 2.x(中阻塞,如果通过进程替换提供其输入文件,例如:
script.py <(cat *.gz)
我们已经使用 gzip 以外的命令(例如 cat(进行了测试,只是为了看看我们是否收到类似的错误。 他们都抱怨/dev/fd/63
(或/dev/fd/63.gz
(不存在。 以下是(简化的(相关代码:
def open_gzip_in(infile):
'''Opens a gzip file for reading, using external gzip if available'''
# Determine whether to use the gzip command line tool or not
if exeExists('gzip'):
cmd = ['gzip', '-dc', infile]
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, bufsize=-1,
universal_newlines=True)
if sys.version.startswith("2"):
with p.stdout:
for line in iter(p.stdout.readline, b''):
yield line
else:
with p:
for line in p.stdout:
yield line
exit_code = p.wait()
if exit_code != 0:
raise subprocess.CalledProcessError(
p.returncode, subprocess.list2cmdline(cmd), 'Ungzip failed')
else:
with io.TextIOWrapper(io.BufferedReader(gzip.open(infile))) as f:
for line in f:
yield(line)
顺便说一下,我们做分叉只是因为命令行 gzip 比使用 gzip.open 快得多,而且我们的脚本是一个长时间运行的工作线程 - 差异是几个小时。
我们正在为这个问题实施一个变通方法,但想了解为什么它在 python 3 中不起作用,但在 python 2 中确实有效。
新的默认Popen()
-family参数close_fds=True
的副作用。您可以使用 close_fds=False
显式覆盖它,并且继承的文件描述符将传递到子进程(取决于通过 os.set_inheritable()
进行配置(。
同样,在 Python 3.2 及更高版本中,您可以使用pass_fds
列表(如 in、pass_fds=[0,1,2,63]
(使 stdin、stdout、stderr 和 FD #63 可用于调用的子进程。