子进程和字符串类型不支持缓冲区API

21

我有

cmd = subprocess.Popen('dir',shell=True,stdout=subprocess.PIPE)
for line in cmd.stdout:
  columns = line.split(' ')
  print (columns[3])

第3行出现错误,类型Str不支持缓冲区API。

我做错了什么?我使用的是Python 3.3版本。

3个回答

21

您正在读取二进制数据,而不是str,因此您需要首先对输出进行解码。 如果将universal_newlines参数设置为True,则使用 locale.getpreferredencoding()方法的结果(与打开文本文件相同)自动解码stdout

cmd = subprocess.Popen(
    'dir', shell=True, stdout=subprocess.PIPE, universal_newlines=True)
for line in cmd.stdout:
    columns = line.decode().split()
    if columns:
        print(columns[-1])

如果您使用Python 3.6或更高版本,您可以在Popen()调用中使用显式的encoding参数来指定不同的编解码器,例如UTF-8:
cmd = subprocess.Popen(
    'dir', shell=True, stdout=subprocess.PIPE, encoding='utf8')
for line in cmd.stdout:
    columns = line.split()
    if columns:
        print(columns[-1])

如果您需要在Python 3.5或更早版本中使用不同的编解码器,请不要使用universal_newlines,而是明确地从字节解码文本。

您试图使用str参数拆分bytes值:

>>> b'one two'.split(' ')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Type str doesn't support the buffer API

通过解码,您可以避免这个问题,而且您的print()调用也不必在输出前加上b'..'

然而,您可能只想使用os模块来获取文件系统信息:

import os

for filename in os.listdir('.'):
    print(filename)

谢谢,它可以工作了。但是你有任何想法为什么我会得到列表索引超出范围的错误吗? - Nick Loach
@NickLoach:这行少于3列? - Martijn Pieters
谢谢Martijn,该行代码为['03-04-2013', '', '19: 48', '', '', '', '<DIR>', '', '', '', '', '', '', '', '', '', '.ipython\r\n'],我想要名为.ipython的字段作为输出。 - Nick Loach
我制作了一个简单的示例,我将运行的命令不是操作系统命令,而是一个输出类似于上述内容的二进制文件。 - Nick Loach
当我像你的代码一样使用[-1]时,出现了IndexError: list index out of range :-)。 - Nick Loach
显示剩余3条评论

4
Martijn Pieters 的回答 的第一部分简化的方法是在 Popen 调用时传递 universal_newlines=True 参数。
甚至还可以将其简化为:
output = subprocess.check_output('dir', universal_newlines=True)
columns = output.split()
print(columns)

注意:如果文件或目录名包含空格,请使用os.listdir('.') ,如Martijn Pieters的答案中所建议的,或者类似以下内容的方法:

output = subprocess.check_output('dir', universal_newlines=True)
columns = []
for e in output.split():
    if len(columns) > 0 and columns[-1].endswith('\\'):
        columns[-1] = columns[-1][:-1] + " " + e
    else:
        columns.append(e)
print(columns)

“dir”是Windows上的内部命令,即如果没有“dir.exe”,则在这种情况下需要使用“shell=True”。 - jfs
如果我们假设输出为单列,则可以使用.splitlines()来处理带有空格的路径。 - jfs
@J.F.Sebastian,同意。我没有使用.splitlines(),因为我的dir版本(GNU coreutils 8.21)会产生多列输出。 - tjanez
如果stdout被重定向(如stdout=PIPE),则dir会产生单列输出。尝试在终端中输入dir | cat以查看结果。 - jfs

0
最好使用 binascii.b2a_uu,将二进制数据转换为 ASCII 字符串。
from binascii import b2a_uu 
cmd = b2a_uu(subprocess.Popen('dir',shell=True,stdout=subprocess.PIPE))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接