【问题标题】:Why does `script.py <(cat *.gz)` work with subprocess.Popen in python 2 but not python 3?为什么`script.py <(cat *.gz)`在python 2中与subprocess.Popen一起工作,但在python 3中却不行?
【发布时间】:2019-11-27 15:03:48
【问题描述】:

我们最近发现,如果我们通过进程替换为其输入文件提供输入文件,我们开发的脚本会在 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)

顺便说一下,我们进行 fork 只是因为命令行 gzip 比使用 gzip.open 快得多,而且我们的脚本是一个长时间运行的工作程序 - 差异是几个小时。

我们正在解决这个问题,但想了解为什么它在 python 3 中不起作用,但在 python 2 中起作用。

【问题讨论】:

  • 顺便说一句,如果解压速度对你来说成本很高,你可能会考虑使用 gzip 以外的算法。查看facebook.github.io/zstd中的基准
  • Gzip 不是我们的选择。它由运行我们的脚本的系统使用。

标签: python subprocess process-substitution


【解决方案1】:

这是新的默认 Popen()-family 参数 close_fds=True 的副作用。您可以使用close_fds=False 显式覆盖它,并且您继承的文件描述符将传递给子进程(通过os.set_inheritable() 进行配置)。

同样,在 Python 3.2 及更高版本上,您可以使用 pass_fds 列表(如 pass_fds=[0,1,2,63])使 stdin、stdout、stderr 和 FD #63 可用于调用的子进程。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-04-22
    • 2021-03-23
    • 1970-01-01
    • 2017-03-12
    • 2016-08-26
    • 1970-01-01
    • 2019-12-26
    相关资源
    最近更新 更多