2009-04-22 46 views
7

Mã này tạo ra "AttributeError: đối tượng 'Popen' không có thuộc tính 'fileno" khi chạy với Python 2.5.1Python subprocess "đối tượng không có thuộc tính 'fileno'" lỗi

Code:

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() 

stack:

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() 

mã này sẽ được làm việc python tài liệu mô tả this usage.

+4

Bế mạc này là "quá cục bộ" là vô lý như * Tôi * thấy nó hữu ích, năm sau đó. Đây là bản chất của các vấn đề lập trình - chúng luôn là vấn đề thích hợp áp dụng cho một userbase hẹp. Nhưng chúng rất hữu ích ... SO khiến tôi buồn những ngày này, đó là lý do tại sao tôi không thường xuyên sử dụng nó nữa. – Dan

Trả lời

10

Ba điều

Đầu tiên,() của bạn là sai.

Thứ hai, kết quả của subprocess.Popen() là đối tượng quá trình, không phải tệp.

proc = [] 
proc.append(Popen(['svn', 'blame', shellquote(filename)], stdout=PIPE)) 
proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1]), stdout=PIPE) 

Giá trị của proc[-1] không phải là tệp, đó là quá trình chứa tệp.

proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1].stdout, stdout=PIPE)) 

Thứ ba, không làm tất cả những gì trcut rác trong vỏ, vài điều có thể chậm hơn. Viết xử lý trcut bằng Python - nó nhanh hơn và đơn giản hơn.

+0

Là 'Cây' cho sự hài hước - hay bạn có nghĩa là "Ba"? –

+0

Cảm ơn bạn đã bình luận. Nó đã được sửa. –

+0

Tập lệnh sẽ chỉ được sử dụng trên Linux. Đó là nhiều hơn một công cụ một off hơn bất cứ điều gì khác. Sử dụng các công cụ vỏ đã được dễ dàng hơn so với cố gắng tìm ra tương đương python. Chỉ cần thả vào một lệnh shell đang làm việc thì dễ hơn là gỡ lỗi regex. – epochwolf

-1

trông giống như lỗi cú pháp. ngoại trừ lần đầu tiên nối thêm phần còn lại là sai (các khung đánh giá).

+0

Nó không thực sự. – epochwolf

+0

nó không phải là những gì? mỗi câu trả lời bao gồm một thực tế rằng bạn có một vấn đề với dấu ngoặc của bạn! nó là một vấn đề cú pháp, trên đỉnh của đường ống. – SilentGhost

+0

Đó là một vấn đề cú pháp nhưng không phải là một lỗi cú pháp kỳ lạ đủ. Mã được dán trực tiếp từ tập lệnh mà tôi có. Lỗi là lỗi đã được tạo. Một lỗi cú pháp sẽ giết chết tập lệnh ở giữa định nghĩa hàm. Hàm thực sự được gọi. Tôi nghĩ rằng đó là bởi vì list.append() chấp nhận nhiều đối số. – epochwolf

1

Bạn muốn stdout của quá trình này, vì vậy thay thế của bạn stdin=proc[-1] với stdin=proc[-1].stdout

Ngoài ra, bạn cần phải di chuyển Dấu ngoặc đơn của bạn, nó sẽ đến sau khi tranh luận stdout.

proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1]), stdout=PIPE) 

nên là:

proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1].stdout, stdout=PIPE)) 

Fix của bạn append cuộc gọi khác trong cùng một cách.

3

Có một vài điều lạ trong kịch bản,

  • Tại sao các bạn lưu trữ mỗi quá trình trong một danh sách? Nó sẽ không thể đọc được nhiều hơn chỉ đơn giản là sử dụng các biến? Loại bỏ tất cả các .append()s tiết lộ một lỗi cú pháp, nhiều lần bạn đã trôi qua stdout = PIPE đến append đối số, thay vì Popen:

    proc.append(Popen(...), stdout=PIPE) 
    

    Vì vậy, một thẳng-viết lại (vẫn có lỗi, tôi sẽ đề cập đến trong một giây) sẽ trở thành ..

    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() 
    
  • Trên mỗi lệnh tiếp theo, bạn đã vượt qua đối tượng Popen, không rằng xử lý stdout. Từ phần "Replacing shell pipeline" của tài liệu quy trình con, bạn làm ..

    p1 = Popen(["dmesg"], stdout=PIPE) 
    p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) 
    

    ..trong khi bạn đang làm tương đương với stdin=p1.

    Các tr1 = (trong mã viết lại ở trên) dòng sẽ trở thành ..

    tr1 = Popen(['tr', '-s', r"'\040'"], stdin=blame.stdout, stdout=PIPE) 
    
  • Bạn không cần phải thoát khỏi lệnh/tranh luận với tiến trình con, như tiến trình con không chạy lệnh trong bất kỳ vỏ (trừ bạn chỉ định shell=True). Xem phần Security của tài liệu quy trình con.

    Thay vì ..

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

    ..you có thể yên tâm làm ..

    Popen(['svn', 'blame', filename], stdout=PIPE) 
    
  • Như S. Lott đề nghị, không sử dụng tiến trình con để làm text-thao tác dễ dàng hơn thực hiện trong Python (các lệnh tr/cut). Đối với một, tr/cắt vv không phải là cực kỳ di động (phiên bản khác nhau có đối số khác nhau), cũng có thể là khá khó đọc (Tôi không có ý tưởng những gì tr và cắt đang làm)

    Nếu tôi viết lại lệnh, tôi có lẽ sẽ làm một cái gì đó giống như ..

    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

giống như S. Lott nói, xử lý văn bản trong Python là tốt hơn.

Nhưng nếu bạn muốn sử dụng các tiện ích cmdline, bạn có thể giữ cho nó có thể đọc được bằng cách sử dụng 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] 
+1

Có một rủi ro bảo mật lớn khi sử dụng shell = True, cũng như khả năng thực sự rằng quá trình có thể treo giữa dữ liệu đường ống từ một lệnh đến lệnh tiếp theo mà không cung cấp bất kỳ xử lý lỗi thực sự nào. – SummerEla

+0

@SummerEla đủ công bằng, mặc dù đó là một sự cân bằng - mọi người làm điều đó trong các kịch bản shell mọi lúc và chấp nhận nó ở đó. Có thể thực hiện cùng một sự cân bằng khi viết kịch bản bằng Python. Tính dễ đọc và đơn giản cũng có giá trị của chúng – orip

Các vấn đề liên quan