将StringIO用作Popen的stdin

Suz*_*ron 12 python subprocess

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

#!/bin/bash

(cat somefile 2>/dev/null || (echo 'somefile not found'; cat logfile)) \
| grep .
Run Code Online (Sandbox Code Playgroud)

我尝试了这个(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)
Run Code Online (Sandbox Code Playgroud)

但是我得到了错误AttributeError: StringIO instance has no attribute 'fileno'.

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

Ski*_*Ski 9

p = subprocess.Popen(['grep', '...'], stdin=subprocess.PIPE, 
                                      stdout=subprocess.PIPE)
output, output_err = p.communicate(myfile.read())
Run Code Online (Sandbox Code Playgroud)

  • 这不是将`myfile`的全部内容读入内存,为它分配字符串等吗?难道不存在将文件句柄直接传递给下一个进程的方法吗? (5认同)

jfs*_*jfs 5

不要使用 bare 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)
Run Code Online (Sandbox Code Playgroud)

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

如果您有一个类似文件的对象(没有 real .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()
Run Code Online (Sandbox Code Playgroud)