7

此代码在使用 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 文档中描述此用法

4

5 回答 5

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处理——它更快更简单。

于 2009-04-22T16:15:09.047 回答
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()
    
  • 在每个后续命令中,您都传递了 Popen 对象,而不是processes stdout。从子流程文档的“替换 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)。请参阅子流程文档的安全部分。

    代替..

    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
    
于 2009-04-22T17:13:09.420 回答
1

您想要该过程的标准输出,因此将您的替换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呼叫。

于 2009-04-22T16:11:54.997 回答
-1

看起来像语法错误。除了第一个附加之外,其余都是错误的(复习括号)。

于 2009-04-22T16:11:38.353 回答
-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]
于 2009-04-22T18:01:31.177 回答