Python Popen.communicate()。 TypeError:预期的字符串或缓冲区,而不是List

时间:2016-03-03 10:12:24

标签: python awk subprocess popen communicate

CONTEXT

代码应该使用awk获取文件对象并从中提取信息。

它使用带有'pieceSize'的readlines()作为参数。 'pieceSize'是我希望readlines()在通过文件时使用的MB数。我这样做是希望我的程序不会遇到麻烦,如果需要读取的文件比我的计算机内存大得多。 正在读取的文件有很多行和列。

以下代码尝试使用awk从第一行读取第一个字段。

import os
from subprocess import Popen, PIPE, STDOUT

def extract_info(file_object):
    pieceSize = 16777216 # 16MB
    for line in file_object.readlines(pieceSize):
        eachline = line.rsplit() # removing extra returns
        p = Popen(['awk','{{print `$`1}}'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)
        pOut = p.communicate(input=eachline)[0]  
        print(pOut.decode())

错误消息

我收到的错误类似于......

... in _communicate_with_poll(self, input)
chunk = input[input_offset : input_offset + _PIPE_BUF]
try:
-> input_offset += os.write(fd, chunk)
except OSError as e:
if e.errno == errno.EPIPE:
TypeError: must be string or buffer, not list

3 个答案:

答案 0 :(得分:1)

发生错误是因为str.rsplit()返回列表,但Popen.communicate()需要字符串(或缓冲区)。因此,您无法将eachline的结果传递给communicate()

这就是问题的原因,但我不确定你为什么要拆分线路。 rsplit()将拆分所有空白,包括空格,制表符等。这真的是你想要的吗?

此外,此代码将迭代readlines()返回的第一组行。文件的其余部分仍未处理。你需要一个外部循环来保持运行,直到输入文件耗尽(可能你的调用代码中没有显示?)。然后它为每一行输入调用Popen一次,效率非常低。

我建议你完全用Python处理这个处理。 line.split()[0]有效地为您提供所需的数据(文件的第一列),而不将其传递给awk。逐行迭代可以提高内存效率。

也许发电机是更好的解决方案:

def extract_info(file_object):
    for line in file_object:
        yield line.split()[0]

然后你可以在调用代码中迭代它:

with open('inputfile') as f:
    for first_field in extract_info(f):
        print first_field

答案 1 :(得分:0)

您需要将从split返回的列表中的字符串传递给input:

 pOut, _ = p.communicate(input=eachline[0])

你正在传递line.rsplit()即列表,不知道你想要准确传递什么,也许你想要input=" ".join(eachline)但不管它是什么,它应该是一个字符串而不是你传递给输入的列表本身。你的awk语法似乎也不正确。

您还可以迭代文件对象本身,逐行避免读取行。

for line in file_object:  

所以整个代码就像:

def extract_info(file_object):
    for line in file_object:
        eachline = line.rsplit() # removing extra returns
        p = Popen(['awk','{print $1}'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)
        pOut,_ = p.communicate(input=" ".join(eachline))
        print(pOut.decode())

显然要修复eachline逻辑以执行您希望它做的任何事情。

另一方面,根本不需要使用awk,你可以用python完成所有这些。

def extract_info(file_object):
    for line in file_object:
        eachline = line.split(None, 1)
        print(eachline[0])

或者更简洁地使用python3的map和扩展的可迭代解包:

def extract_info(file_object):
    for i, *_ in map(str.split, file_object):
        print(i)

答案 2 :(得分:0)

目前还不完全清楚您期望实现的输出。

但是,这可能会有所帮助:

  • 为什么要使用awk如果你所做的只是打印一行中的第一个单词,你可以使用python。
  • 如果您想阅读尺寸大于内存的文件,可以使用readlinefor line in file_handler加载每一行,则应避免使用readlines()和{{1}加载整个文件。

试试这个:

read()