Python subprocess “对象没有属性 'fileno'” 错误
这段代码在使用 Python 2.5.1 运行时,会出现“AttributeError: 'Popen' 对象没有 'fileno' 属性”的错误。
代码:
def get_blame(filename):
proc = []
proc.append(Popen(['svn', 'blame', shellquote(filename)], stdout=PIPE))
proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1]), stdout=PIPE)
proc.append(Popen(['tr', r"'\040'", r"';'"], stdin=proc[-1]), stdout=PIPE)
proc.append(Popen(['cut', r"-d", r"\;", '-f', '3'], stdin=proc[-1]), stdout=PIPE)
return proc[-1].stdout.read()
堆栈信息:
function walk_folder in blame.py at line 55
print_file(os.path.join(os.getcwd(), filename), path)
function print_file in blame.py at line 34
users = get_blame(filename)
function get_blame in blame.py at line 20
proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1]), stdout=PIPE)
function __init__ in subprocess.py at line 533
(p2cread, p2cwrite,
function _get_handles in subprocess.py at line 830
p2cread = stdin.fileno()
这段代码应该是可以正常工作的,Python 文档中描述了 这种用法。
5 个回答
你想要获取这个进程的标准输出,所以把你的 stdin=proc[-1]
改成 stdin=proc[-1].stdout
。
另外,你需要调整一下括号的位置,它应该放在 stdout
参数之后。
proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1]), stdout=PIPE)
应该是:
proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1].stdout, stdout=PIPE))
同样的方式修正你其他的 append
调用。
这个脚本里有一些奇怪的地方,
你为什么要把每个进程都存储在一个列表里呢?直接用变量不是更容易理解吗?如果去掉所有的
.append()
,会出现语法错误,因为你在多次调用append
的时候,把stdout=PIPE
传给了append
的参数,而不是传给Popen
:proc.append(Popen(...), stdout=PIPE)
所以直接重写(虽然还有我稍后会提到的错误)会变成..
def get_blame(filename): blame = Popen(['svn', 'blame', shellquote(filename)], stdout=PIPE) tr1 = Popen(['tr', '-s', r"'\040'"], stdin=blame, stdout=PIPE) tr2 = Popen(['tr', r"'\040'", r"';'"], stdin=tr1), stdout=PIPE) cut = Popen(['cut', r"-d", r"\;", '-f', '3'], stdin=tr2, stdout=PIPE) return cut.stdout.read()
在每个后续的命令中,你传递的是 Popen 对象,而不是那个进程的
stdout
。根据 subprocess 文档中 “替代 shell 管道” 的部分,你应该这样做..p1 = Popen(["dmesg"], stdout=PIPE) p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
..而你做的相当于
stdin=p1
。上面重写代码中的
tr1 =
这一行应该变成..tr1 = Popen(['tr', '-s', r"'\040'"], stdin=blame.stdout, stdout=PIPE)
你不需要用 subprocess 来转义命令/参数,因为 subprocess 并不会在任何 shell 中运行命令(除非你指定
shell=True
)。可以查看 subprocess 文档中的 安全性部分。与其这样..
proc.append(Popen(['svn', 'blame', shellquote(filename)], stdout=PIPE))
..你可以安全地这样做..
Popen(['svn', 'blame', filename], stdout=PIPE)
正如 S.Lott 建议的,不要用 subprocess 来处理那些在 Python 中更容易完成的文本操作(比如 tr/cut 命令)。首先,tr/cut 等命令并不是特别通用(不同版本有不同的参数),而且它们的可读性也很差(我完全不知道 tr 和 cut 在干什么)
如果我要重写这个命令,我可能会这样做..
def get_blame(filename): blame = Popen(['svn', 'blame', filename], stdout=PIPE) output = blame.communicate()[0] # preferred to blame.stdout.read() # process commands output: ret = [] for line in output.split("\n"): split_line = line.strip().split(" ") if len(split_line) > 2: rev = split_line[0] author = split_line[1] line = " ".join(split_line[2:]) ret.append({'rev':rev, 'author':author, 'line':line}) return ret
三件事
首先,你的 () 用得不对。
其次,subprocess.Popen()
的结果是一个进程对象,而不是一个文件。
proc = []
proc.append(Popen(['svn', 'blame', shellquote(filename)], stdout=PIPE))
proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1]), stdout=PIPE)
proc[-1]
的值不是文件,而是包含这个文件的进程。
proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1].stdout, stdout=PIPE))
第三,不要在命令行里搞那些 tr
和 cut
的东西,这样做会很慢。把 tr
和 cut
的处理写在 Python 里——这样更快也更简单。