【问题标题】:Use StringIO as stdin with Popen使用 StringIO 作为 Popen 的标准输入
【发布时间】:2014-01-01 07:35:54
【问题描述】:

我有以下我想用 Python 编写的 shell 脚本(当然grep . 实际上是一个更复杂的命令):

#!/bin/bash

(cat somefile 2>/dev/null || (echo 'somefile not found'; cat logfile)) \
| grep .

我试过这个(无论如何它都缺少与cat logfile 等效的东西):

#!/usr/bin/env python

import StringIO
import subprocess

try:
    myfile = open('somefile')
except:
    myfile = StringIO.StringIO('somefile not found')

subprocess.call(['grep', '.'], stdin = myfile)

但我收到错误 AttributeError: StringIO instance has no attribute 'fileno'

我知道我应该使用 subprocess.communicate() 而不是 StringIO 将字符串发送到 grep 进程,但我不知道如何混合字符串和文件。

【问题讨论】:

  • 不能使用StringIO对象来提供流程输入;请改用subprocess.PIPE
  • @MartijnPieters 正如我所说的(最后一句),“我知道我应该使用 subprocess.communicate() 而不是 StringIO 将字符串发送到 grep 进程,但我不知道如何混合使用两者字符串和文件。”
  • 为什么不从打开的文件对象中读取,写入管道?如果没有打开的文件,写替代文本。
  • 为什么不使用 grep?
  • 哦,好的。你可以使用一些库以完全 Python 的方式来完成它。但我理解你的意思。

标签: python subprocess


【解决方案1】:
p = subprocess.Popen(['grep', '...'], stdin=subprocess.PIPE, 
                                      stdout=subprocess.PIPE)
output, output_err = p.communicate(myfile.read())

【讨论】:

  • 这不是将myfile的全部内容读入内存,为它分配一个字符串等等吗?不应该有办法将文件句柄直接传递给下一个进程吗?
【解决方案2】:

不要使用裸except,它可能会捕获太多。在 Python 3 中:

#!/usr/bin/env python3
from subprocess import check_output

try:
    file = open('somefile', 'rb', 0)
except FileNotFoundError:
    output = check_output(cmd, input=b'somefile not found')
else:
    with file:
        output = check_output(cmd, stdin=file)

它适用于大文件(文件在文件描述符级别重定向 - 无需将其加载到内存中)。

如果你有一个类似文件的对象(没有真正的.fileno());您可以使用.write() 方法直接写入管道:

#!/usr/bin/env python3
import io
from shutil import copyfileobj
from subprocess import Popen, PIPE
from threading import Thread

try:
    file = open('somefile', 'rb', 0)
except FileNotFoundError:
    file = io.BytesIO(b'somefile not found')

def write_input(source, sink):
    with source, sink:
        copyfileobj(source, sink)

cmd = ['grep', 'o']
with Popen(cmd, stdin=PIPE, stdout=PIPE) as process:
    Thread(target=write_input, args=(file, process.stdin), daemon=True).start()
    output = process.stdout.read()

【讨论】:

  • copyfileobj() 的巧妙建议。您能否编辑您的答案以澄清为什么需要线程?我想在读取processes 输出时需要避免死锁。
  • @smbear 如果不使用线程(或 async.io),您认为会发生什么?
【解决方案3】:

下面的答案也使用shutil——效率很高——, 但避免了运行一个单独的线程,而当标准输入结束时,它又永远不会结束并变成僵尸(就像@jfs的答案一样)

import os 
import subprocess
import io
from shutil import copyfileobj

file_exist = os.path.isfile(file)
with open(file) if file_exists else io.StringIO("Some text here ...\n") as string_io:
    with subprocess.Popen("cat", stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True) as process:
        copyfileobj(string_io, process.stdin)
        # the subsequent code is not executed until copyfileobj ends, 
        # ... but the subprocess is effectively using the input.

        process.stdin.close()  # close or otherwise won't end

        # Do some online processing to process.stdout, for example...
        for line in process.stdout:
            print(line) # do something

如果已知输出适合内存,则可以关闭和解析:

        ...
        stdout_text , stderr_text = process.communicate()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-13
    • 2011-09-15
    • 2020-08-13
    • 2010-09-21
    • 2011-08-27
    • 1970-01-01
    • 1970-01-01
    • 2012-11-08
    相关资源
    最近更新 更多