2009-03-11 19 views
81

Tôi cần chạy lệnh shell không đồng bộ từ tập lệnh Python. Điều này có nghĩa là tôi muốn tập lệnh Python của tôi tiếp tục chạy trong khi lệnh bên ngoài tắt và thực hiện bất cứ điều gì cần làm.Làm cách nào để chạy lệnh ngoài không đồng bộ từ Python?

Tôi đọc bài này:

Calling an external command in Python

sau đó tôi đã đi ra và đã làm một số thử nghiệm, và có vẻ như os.system() sẽ thực hiện công việc với điều kiện là tôi sử dụng & vào cuối của lệnh để tôi không phải đợi nó quay trở lại. Những gì tôi đang tự hỏi là nếu đây là cách thích hợp để thực hiện một điều như vậy? Tôi đã thử commands.call() nhưng nó sẽ không làm việc cho tôi bởi vì nó chặn trên lệnh bên ngoài.

Vui lòng cho tôi biết nếu sử dụng os.system() cho điều này là được khuyến khích hoặc nếu tôi nên thử một số tuyến đường khác.

Trả lời

90

subprocess.Popen thực hiện chính xác những gì bạn muốn.

from subprocess import Popen 
p = Popen(['watch', 'ls']) # something long running 
# ... do other stuff while subprocess is running 
p.terminate() 

(Chỉnh sửa để hoàn thành câu trả lời từ comments)

Các Popen dụ có thể làm những việc khác nhau như bạn có thể poll() nó để xem nếu nó vẫn đang chạy, và bạn có thể communicate() với nó để gửi nó dữ liệu trên stdin và chờ cho nó chấm dứt.

+2

Bạn cũng có thể sử dụng thăm dò ý kiến ​​() để kiểm tra xem quá trình con đã chấm dứt hay sử dụng wait() để chờ kết thúc. –

+0

Adam, rất đúng, mặc dù nó có thể là tốt hơn để sử dụng giao tiếp() để chờ đợi bởi vì đó là xử lý tốt hơn các bộ đệm vào/ra và có những tình huống mà lũ lụt có thể chặn. –

+0

Adam: tài liệu nói "Cảnh báo Điều này sẽ bế tắc nếu quá trình con tạo ra đủ đầu ra cho đường ống stdout hoặc stderr sao cho nó chặn bộ đệm ống hệ điều hành chấp nhận nhiều dữ liệu hơn. Sử dụng giao tiếp() để tránh điều đó. " Tuy nhiên, –

36

Nếu bạn muốn chạy nhiều quá trình song song và sau đó xử lý chúng khi họ mang lại kết quả, bạn có thể sử dụng phiếu như thế nào trong những điều sau đây:

from subprocess import Popen, PIPE 
import time 

running_procs = [ 
    Popen(['/usr/bin/my_cmd', '-i %s' % path], stdout=PIPE, stderr=PIPE) 
    for path in '/tmp/file0 /tmp/file1 /tmp/file2'.split()] 

while running_procs: 
    for proc in running_procs: 
     retcode = proc.poll() 
     if retcode is not None: # Process finished. 
      running_procs.remove(proc) 
      break 
     else: # No process is done, wait a bit and check again. 
      time.sleep(.1) 
      continue 

    # Here, `proc` has finished with return code `retcode` 
    if retcode != 0: 
     """Error handling.""" 
    handle_results(proc.stdout) 

Các dòng điều khiển có một chút phức tạp bởi vì tôi m cố gắng làm cho nó nhỏ - bạn có thể cấu trúc lại theo sở thích của bạn. :-)

Điều này có lợi thế là phục vụ các yêu cầu hoàn thiện sớm trước. Nếu bạn gọi communicate trong quá trình chạy đầu tiên và hóa ra là chạy dài nhất, các quy trình đang chạy khác sẽ không hoạt động khi bạn có thể xử lý kết quả của chúng.

+3

@Tino Tùy thuộc vào cách bạn định nghĩa là chờ đợi. Xem [Sự khác biệt giữa bận-chờ và bỏ phiếu là gì?] (Http://stackoverflow.com/questions/10594426/) –

+1

Có cách nào để thăm dò ý kiến ​​một tập hợp các quy trình không chỉ một quá trình không? –

+1

lưu ý: nó có thể treo nếu một quá trình tạo ra đủ đầu ra. Bạn nên sử dụng đồng thời stdout nếu bạn sử dụng PIPE (có quá nhiều nhưng không đủ) cảnh báo trong tài liệu của quy trình con về nó). – jfs

8

Điều tôi tự hỏi là liệu [os.system()] này có phải là cách thích hợp để thực hiện một điều như vậy không?

No. os.system() không phải là cách thích hợp. Đó là lý do tại sao mọi người nói sử dụng subprocess.

Để biết thêm thông tin, đọc http://docs.python.org/library/os.html#os.system

Các module subprocess cung cấp nhiều phương tiện mạnh mẽ để đẻ trứng quy trình mới và lấy kết quả của họ; sử dụng mô-đun đó là thích hợp hơn khi sử dụng chức năng này. Sử dụng mô-đun xử lý con. Kiểm tra đặc biệt là Thay thế Cũ hơn Chức năng với mô-đun quy trình phụ .

6

Tôi đã thành công tốt với mô-đun asyncproc, giao dịch độc đáo với đầu ra từ các quy trình. Ví dụ:

import os 
from asynproc import Process 
myProc = Process("myprogram.app") 

while True: 
    # check to see if process has ended 
    poll = myProc.wait(os.WNOHANG) 
    if poll is not None: 
     break 
    # print any new output 
    out = myProc.read() 
    if out != "": 
     print out 
+0

cuộc thăm dò ý kiến! = Không: ??? Không nên là 'nếu thăm dò ý kiến ​​không phải là Không:'? –

+0

vâng, chắc chắn - làm cho các vòng mã bốn tuổi? :) – Noah

+0

là nơi này trên github? – Nick

3

Tôi gặp sự cố tương tự khi cố gắng kết nối với thiết bị đầu cuối 3270 bằng phần mềm tạo kịch bản s3270 bằng Python. Bây giờ tôi đang giải quyết vấn đề với một lớp con của Process mà tôi tìm thấy ở đây:

http://code.activestate.com/recipes/440554/

Và đây là các mẫu lấy từ file:

def recv_some(p, t=.1, e=1, tr=5, stderr=0): 
    if tr < 1: 
     tr = 1 
    x = time.time()+t 
    y = [] 
    r = '' 
    pr = p.recv 
    if stderr: 
     pr = p.recv_err 
    while time.time() < x or r: 
     r = pr() 
     if r is None: 
      if e: 
       raise Exception(message) 
      else: 
       break 
     elif r: 
      y.append(r) 
     else: 
      time.sleep(max((x-time.time())/tr, 0)) 
    return ''.join(y) 

def send_all(p, data): 
    while len(data): 
     sent = p.send(data) 
     if sent is None: 
      raise Exception(message) 
     data = buffer(data, sent) 

if __name__ == '__main__': 
    if sys.platform == 'win32': 
     shell, commands, tail = ('cmd', ('dir /w', 'echo HELLO WORLD'), '\r\n') 
    else: 
     shell, commands, tail = ('sh', ('ls', 'echo HELLO WORLD'), '\n') 

    a = Popen(shell, stdin=PIPE, stdout=PIPE) 
    print recv_some(a), 
    for cmd in commands: 
     send_all(a, cmd + tail) 
     print recv_some(a), 
    send_all(a, 'exit' + tail) 
    print recv_some(a, e=0) 
    a.wait() 
6

Sử dụng Pexpect [http://www.noah.org/wiki/Pexpect] với phi chặn đường dây đọc là một cách khác để thực hiện việc này. Pexpect giải quyết các vấn đề bế tắc, cho phép bạn dễ dàng chạy các quy trình trong nền và cung cấp các cách dễ dàng để gọi lại khi quá trình của bạn trích ra các chuỗi được xác định trước và thường tương tác với quy trình dễ dàng hơn nhiều.

+0

pexpect là một cách không chính thống để chạy các quy trình, nhưng tôi thấy nó khá thực tế. – droope

2

Xét "Tôi không cần phải chờ đợi cho nó để trở về", một trong những giải pháp đơn giản nhất sẽ là thế này:

subprocess.Popen(\ 
    [path_to_executable, arg1, arg2, ... argN], 
    creationflags = subprocess.CREATE_NEW_CONSOLE, 
).pid 

Nhưng ... Từ những gì tôi đọc không phải là "một cách thích hợp để thực hiện một điều như vậy "vì những rủi ro bảo mật được tạo bởi cờ subprocess.CREATE_NEW_CONSOLE.

Điều quan trọng xảy ra ở đây là sử dụng subprocess.CREATE_NEW_CONSOLE để tạo bảng điều khiển mới và .pid (trả về ID tiến trình để bạn có thể kiểm tra chương trình sau này nếu muốn) để không đợi chương trình kết thúc công việc.

+0

cho biết mỗi tham số trong phương thức con. Phương thức mở là rất hữu ích –