“Python子流程”;对象没有属性';文件编号'&引用;错误

“Python子流程”;对象没有属性';文件编号'&引用;错误,python,pipe,subprocess,Python,Pipe,Subprocess,此代码在使用Python 2.5.1运行时生成“AttributeError:'Popen'对象没有属性'fileno'” 代码: 堆栈: 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

此代码在使用Python 2.5.1运行时生成“AttributeError:'Popen'对象没有属性'fileno'”

代码:

堆栈:

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文档的描述工作。

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

您想要进程的标准输出,因此请将您的
stdin=proc[-1]
替换为
stdin=proc[-1]。标准输出

此外,您需要移动参数,它应该位于
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))
以相同的方式修复其他
追加
调用。

三件事

首先,你的()是错误的

其次,
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
shell中的垃圾,很少有事情会更慢。用Python编写
tr
cut
处理程序——它更快更简单。

脚本中有一些奇怪的东西

  • 为什么要将每个进程存储在列表中?简单地使用变量不是更易读吗?删除所有
    .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()
    
    tr1 = Popen(['tr', '-s', r"'\040'"], stdin=blame.stdout, stdout=PIPE)
    
  • 在每个后续命令上,您都传递了Popen对象,而不是处理
    stdout
    。在子流程文档的部分中,您可以执行

    p1 = Popen(["dmesg"], stdout=PIPE)
    p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
    
    …而您所做的工作相当于
    stdin=p1

    tr1=
    (在上面重写的代码中)行将变为

    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()
    
    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所建议的那样,不要使用子流程来进行在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
    

正如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]

“树”是幽默的意思吗?还是你的意思是“三”?谢谢你的评论。它已修复。该脚本将仅在linux上使用。这是一个一次性的工具。使用shell工具比试图找出python等价物更容易。只插入一个工作的shell命令比调试正则表达式更容易。解析svn输出不需要复杂的正则表达式。从长远来看,使用shell脚本更为复杂,因为shell语言是一组随机特性的集合。从长远来看,Python更简单。当然,从长远来看。我正在将一个不太好用的bash脚本转换成python,不是什么?每个答案都包括一个事实:你的括号有问题!这是一个语法问题,当然是管道问题。这是一个语法问题,但奇怪的是,这不是一个语法错误。代码是直接从我的脚本粘贴的。错误就是生成的错误。语法错误会杀死函数定义中间的脚本。函数实际上会被调用。我认为这是因为list.append()接受多个参数。不,它不接受。永远不会调用append,因为在初始化Popen时会发生错误,这在错误堆栈中非常明显。是的,这是你的语法错误。没有提出语法错误,但仍然存在语法问题。因此。。。有趣。我最近做了太多编译过的东西。我忘记了python的动态性。多年后,我发现它很有用,所以以“太本地化”来结束它是荒谬的。这就是编程问题的本质——它们总是适用于狭窄用户群的小生境问题。但是它们很有用。。。这几天让我很难过,这就是为什么我不再经常使用它的原因。使用shell=True存在巨大的安全风险,而且该过程很有可能在从一个命令到下一个命令的管道数据之间挂起,而不提供任何真正的错误处理。@Summeela fair,尽管这是一种折衷——人们总是在shell脚本中进行,并在那里接受它。在使用Python编写脚本时,也可以进行同样的权衡。可读性和简单性也有其价值