Python子进程“对象没有属性'fileno'”错误

时间:2009-04-22 16:05:06

标签: python pipe subprocess

使用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 docs describe this usage

5 个答案:

答案 0 :(得分:10)

三件事

首先,你的()错了。

其次,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))

第三,不要在shell中完成所有trcut垃圾,很少有东西可能会变慢。在Python中编写trcut处理 - 它更快更简单。

答案 1 :(得分:3)

脚本中有一些奇怪的东西,

  • 为什么要将每个进程存储在列表中?简单地使用变量会不会更具可读性?删除所有.append()s会显示语法错误,有几次您已将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()
    
  • 在每个后续命令中,您已经传递了处理stdout的Popen对象 not 。从子流程文档的"Replacing shell pipeline"部分,您可以执行..

    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)
    
  • 您不需要使用子进程转义命令/参数,因为子进程不会在任何shell中运行该命令(除非您指定shell=True)。请参阅子流程文档的Security部分。

    而不是..

    proc.append(Popen(['svn', 'blame', shellquote(filename)], stdout=PIPE))
    

    ..你可以安全地做.. ..

    Popen(['svn', 'blame', filename], stdout=PIPE)
    
  • 正如S.Lott建议的那样,不要使用子进程在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
    

答案 2 :(得分:1)

您需要该过程的标准输出,因此请将您的stdin=proc[-1]替换为stdin=proc[-1].stdout

此外,你需要移动你的paren,它应该在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来电。

答案 3 :(得分:-1)

看起来像语法错误。除了第一次追加其余的都是错误的(审查括号)。

答案 4 :(得分:-2)

像S.Lott所说,用Python处理文本更好。

但是如果您想使用cmdline实用程序,可以使用shell=True保持其可读性:

cmdline = r"svn blame %s | tr -s '\040' | tr '\040' ';' | cut -d \; -f 3" % shellquote(filename)
return Popen(cmdline, shell=True, stdout=PIPE).communicate()[0]