2012-01-12 70 views
5

Tôi là nhãn hiệu mới với python, đã sử dụng perl trong nhiều năm. Một điều điển hình tôi làm tất cả các thời gian là perl là mở một lệnh như một đường ống và gán đầu ra của nó cho một biến địa phương để xử lý. Nói cách khác:xử lý đầu ra liên tục của lệnh trong python

"open CMD, "$command|"; 
$output=<CMD>; 

một miếng bánh. Tôi nghĩ rằng tôi có thể làm điều gì đó tương tự trong python theo cách này:

args=[command, args...] 
process=subprocess.Popen(args, stdout=subprocess.PIPE) 
output=process.communicate() 

cho đến nay rất tốt. Bây giờ cho câu hỏi lớn ...

Nếu tôi tắt lệnh đó bằng ssh trên nhiều nền tảng, tôi có thể theo dõi các bộ mô tả trong perl bên trong một vòng chọn để xử lý các kết quả khi chúng đến. Tôi đã tìm thấy python chọn và mô-đun thăm dò ý kiến ​​nhưng tôi không hoàn toàn chắc chắn làm thế nào để sử dụng chúng. Các tài liệu cho biết thăm dò ý kiến ​​sẽ có một xử lý tập tin, nhưng khi tôi cố gắng vượt qua biến 'quá trình' ở trên để poll.register() Tôi nhận được một lỗi rằng nó phải là một int hoặc có một phương pháp fileno(). Vì Popen() đã sử dụng thiết bị xuất chuẩn, tôi đã thử gọi

poll.register(process.stdout) 

và nó không còn phát ra lỗi, mà thay vào đó chỉ bị treo.

Bất kỳ đề xuất/chỉ dẫn nào về cách tạo ra thứ gì đó giống như tác phẩm này?

+0

quy trình.stdout sẽ là tập tin xử lý cho đối tượng quá trình đó - Tôi không chắc liệu đó là tất cả những gì bạn cần để làm cho nó hoạt động với bình chọn/chọn, tôi đã không sử dụng chúng. – AdamKG

+0

Ngoài ra, lưu ý rằng Popen.communicate() chặn cho đến khi EOF - có thể bạn sẽ muốn loại bỏ điều đó. – AdamKG

+0

ahh, tôi hiểu rồi. rõ ràng KHÔNG muốn chặn. rất nhiều cấu trúc mới để thử và quấn bộ não của tôi xung quanh. -mark –

Trả lời

7

Sử dụng select.poll: Bạn cần phải pass objects with a fileno method or real file descriptors (integers):

import os, sys, select, subprocess 

args = ['sh', '-c', 'while true; do date; sleep 2; done'] 
p1 = subprocess.Popen(args, stdout=subprocess.PIPE) 
p2 = subprocess.Popen(args, stdout=subprocess.PIPE) 

while True: 
    rlist, wlist, xlist = select.select([p1.stdout, p2.stdout], [], []) 
    for stdout in rlist: 
     sys.stdout.write(os.read(stdout.fileno(), 1024)) 

Bạn sẽ thấy nó dừng lại mỗi hai giây và sau đó sản xuất ra nhiều hơn vì nó đi kèm sẵn. "Bí quyết" là p1.stdout là một đối tượng giống như tệp bình thường với phương thức fileno trả về số mô tả tệp. Đây là tất cả những gì cần thiết bởi select.

Lưu ý rằng tôi đang đọc từ stdout sử dụng os.read thay vì chỉ cần gọi stdout.read. Điều này là do một cuộc gọi như stdout.read(1024) sẽ làm cho chương trình của bạn chờ cho đến khi số byte được yêu cầu đã được đọc. Ít byte hơn chỉ được trả về khi đạt được EOF, nhưng vì không bao giờ đạt được EOF, cuộc gọi stdout.read sẽ chặn cho đến khi đọc ít nhất 1024 byte.

Chức năng này không giống như chức năng os.read, không có gì phải trả về sớm khi có ít byte hơn - nó trả về ngay lập tức với những gì có sẵn. Nói cách khác, nhận được ít hơn 1024 byte trở lại từ os.read(stdout.fileno(), 1024) không phải là dấu hiệu cho thấy stdout đã bị đóng.

Sử dụng select.epoll là gần như giống hệt nhau, ngoại trừ việc bạn có được một "thô" mô tả tập tin (FD) trở lại mà bạn cần os.read để có thể đọc từ:

import os, sys, select, subprocess 

args = ['sh', '-c', 'while true; do date; sleep 2; done'] 
p1 = subprocess.Popen(args, stdout=subprocess.PIPE) 
p2 = subprocess.Popen(args, stdout=subprocess.PIPE) 

poll = select.poll() 
poll.register(p1.stdout) 
poll.register(p2.stdout) 

while True: 
    rlist = poll.poll() 
    for fd, event in rlist: 
     sys.stdout.write(os.read(fd, 1024)) 

Một đóng FD được đánh dấu bằng các select.POLLHUP sự kiện được trả lại. Sau đó bạn có thể gọi phương thức unregister và cuối cùng thoát khỏi vòng lặp khi tất cả các FD được đóng lại. Cuối cùng, hãy để tôi lưu ý rằng tất nhiên bạn có thể tạo một từ điển với ánh xạ từ các bộ mô tả tập tin về các đối tượng giống như tập tin, và do đó trở lại các tiến trình bạn đã khởi chạy.

+0

slick, nhưng tôi không thấy nó chờ đợi ở bất cứ đâu. , lệnh tôi đang chạy là "collectl", một công cụ giám sát tôi đã viết trong perl –

+0

trang web này là một realpain! Tôi muốn cung cấp một phản hồi định dạng tốt hơn và nó sẽ không cho phép tôi trả lời câu hỏi của riêng tôi. Vì vậy, bây giờ tôi phải viết một câu trả lời khó phân tích cú pháp, giải pháp của bạn hoạt động NHƯNG lựa chọn không ngủ. nó liên tục thức dậy và đọc dưới đây trả về 'Không' mà sau đó tôi phải bỏ qua. Tôi mỏng nếu bạn thử ví dụ của bạn với một cái gì đó giống như một lệnh ps mà tạo ra một đầu ra nhiều hơn bạn sẽ thấy những gì tôi có nghĩa là -mark –

+1

Tôi đã thêm một số báo cáo in bạn có thể kích hoạt. Khi tôi làm điều đó ở đây, tôi thấy rằng nó chờ đợi như nó phải trong hai giây, và sau đó nó đọc một số byte từ mỗi mô tả tập tin đã sẵn sàng. Tôi chỉ thay đổi câu trả lời để đọc * lên đến * 1024 byte và nó có vẻ hoạt động tốt ở đây, có nghĩa là cuộc gọi 'os.read' trả về mỗi khi FD hết byte. –

2
import subprocess 

p = subprocess.Popen('apt-get autoclean', stdout=subprocess.PIPE, stderr = None, shell=True) 

for line in iter(p.stdout.readline, ''): 

    print line 

p.stdout.flush() 
p.stdout.close() 

print ("Done") 
+0

không sử dụng 'shell = True' trừ khi cần thiết. Mã của bạn sẽ treo nếu ai đó thay đổi 'print line' thành' print (line) 'và chạy nó trên Python 3.' print line' tăng gấp đôi tất cả các dòng mới. Sử dụng 'bufsize = 1' để cải thiện hiệu suất. '.flush()' là không cần thiết ở đây. Bạn có thể sử dụng 'with'-statement để đóng đường ống. Gọi 'p.wait()' để tránh zombie. Xem [câu trả lời này (mọi ký tự (kể cả dấu phẩy) là vì một lý do nào đó)] (http://stackoverflow.com/a/17698359/4279) – jfs

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