2010-03-09 20 views
10

Tôi có một chương trình tương tác có tên là my_own_exe. Đầu tiên, nó in ra alive, sau đó bạn nhập S\n và sau đó nó in ra alive một lần nữa. Cuối cùng, bạn nhập L\n. Nó thực hiện một số quá trình xử lý và thoát.tại sao python.subprocess treo sau proc.communicate()?

Tuy nhiên, khi tôi gọi nó từ tập lệnh python sau đây, chương trình dường như treo sau khi in ra 'sống động' đầu tiên.

Có ai ở đây cho tôi biết tại sao điều này xảy ra không?

// sau khi đọc những thăng sau (cảm ơn các bạn), tôi sửa đổi mã như sau:

import subprocess 
import time 

base_command = "./AO_FelixStrategy_UnitTest --bats 31441 --chix 12467 --enxutp 31884 --turq 26372 --symbol SOGN --target_date " + '2009-Oct-16' 
print base_command 

proc2 = subprocess.Popen(base_command, shell=True , stdin=subprocess.PIPE,) 

time.sleep(2); 
print "aliv" 
proc2.communicate('S\n') 

print "alive" 
time.sleep(6) 

print "alive" 
print proc2.communicate('L\n') 
time.sleep(6) 

chương trình bây giờ việc suôn sẻ với người đầu tiên đầu vào 'S \ n', nhưng sau đó dừng lại, và tôi là 'L \ n' thứ hai được bỏ qua.

Có ai có thể cho tôi biết tại sao nó như thế này không?

Trả lời

23

Từ docs for communicate:

Tương tác với quá trình: Gửi dữ liệu đến thiết bị nhập chuẩn. Đọc dữ liệu từ stdout và stderr, cho đến khi kết thúc tập tin. Đợi quá trình chấm dứt.

Vì vậy, sau communicate() chạy, quá trình này đã được chấm dứt.

Nếu bạn muốn viết và đọc mà không cần chờ cho quá trình này để ngăn chặn:

  • Đừng bao giờ sử dụng shell=True - nó needlessy gọi một vỏ để lần lượt gọi chương trình của bạn, do đó sẽ là một quá trình khác giữa bạn và chương trình của bạn. Điều đó có rất nhiều tác dụng phụ khó chịu. Mặc định là shell=False vì vậy bạn nên gắn bó với điều đó. Thay đổi dòng Popen của bạn để:

    p = subprocess.Popen(["./AO_FelixStrategy_UnitTest", 
             "--bats", "31441", "--chix", "12467", 
             "--enxutp", "31884", "--turq", "26372", 
             "--symbol", "SOGN", "--target_date", '2009-Oct-16'], 
            stdin=subprocess.PIPE, 
            stdout=subprocess.PIPE) 
    
  • Sử dụng p.stdin.write để viết thư cho quá trình này. Sử dụng p.stdout.read để đọc từ đó.

  • Gọi p.stdout.read nếu không có gì để đọc sẽ chặn. Gọi p.stdin.write nếu bộ đệm ghi đầy sẽ chặn. Vì vậy, bạn phải chắc chắn rằng bạn có một cái gì đó để đọc/ghi - bạn làm điều đó trên hệ điều hành Unix bằng cách sử dụng select. Trên cửa sổ bạn không may phải nghỉ mát để đề. Ít nhất đó là những gì Popen.communicate thực hiện nội bộ.
  • Nếu bạn không viết AO_FelixStrategy_UnitTest sau đó bạn có thể vấn đề bổ sung:
    • Nó có thể được đọc từ một nơi khác, không đạt tiêu chuẩn đầu vào. Một số chương trình đọc trực tiếp từ thiết bị đầu cuối, một số khác sử dụng một số API OS để đọc. Điều đó có nghĩa là dữ liệu được ghi vào stdin sẽ không đi đến chương trình. Điều này thường đúng đối với lời nhắc mật khẩu.
    • Hãy nhớ rằng bạn phải tính đến AO_FelixStrategy_UnitTest bộ đệm.Theo mặc định truyền thông tiêu chuẩn C PIPE được đệm vì vậy bạn có thể không nhìn thấy bất kỳ đầu ra cho đến khi bạn đã đóng phía đầu vào (bằng cách làm p.stdin.close(). Trừ khi AO_FelixStrategy_UnitTest xả đầu ra theo định kỳ.

Dưới đây là một số mã ví dụ, . dựa trên những gì bạn mô tả Nó có thể làm việc tùy thuộc vào cách AO_FelixStrategy_UnitTest được phát triển:

p = subprocess.Popen(["./AO_FelixStrategy_UnitTest", 
         "--bats", "31441", "--chix", "12467", 
         "--enxutp", "31884", "--turq", "26372", 
         "--symbol", "SOGN", "--target_date", '2009-Oct-16'], 
        stdin=subprocess.PIPE, 
        stdout=subprocess.PIPE) 
output = p.communicate('S\nL\n')[0] 
print output 
+0

Không phải quá trình này được khởi động lại mỗi lần gọi 'proc2'? Hoặc không gọi nó vì nó đã in trước hai người họ? Và nếu đó là trường hợp, tại sao là đóng băng sau khi in đầu tiên và không phải là thứ hai? – Anthony

+0

@Anthony: Không. Quá trình này không được khởi động lại. Nó bị đóng băng sau lần in đầu tiên bởi vì 'communication' đang chờ quá trình kết thúc, nhưng quá trình không bao giờ kết thúc bởi vì nó có thể bị kẹt trên dấu nhắc thứ hai (một nơi mà' 'L \ n'' nên được nhập vào). – nosklo

+0

cảm ơn. dù sao, điều này chỉ giải quyết một phần vấn đề, tôi có thể sử dụng giao tiếp chỉ một lần, đúng không? nếu tôi cần tương tác đọc và viết và đọc và viết, "output = p.communicate ('S \ nL \ n') [0]" không thể hoạt động, đúng không? –

3

communicate() đọc dữ liệu từ stdout và stderr cho đến khi end-of-file đạt - Nó chờ đợi. cho đến khi chương trình của bạn thoát.

+0

Vậy phương pháp đúng cho một tương tác back-and-out với một tiến trình con là gì? – Anthony

+0

@ Anthony: Cách đúng là sử dụng 'p.stdin.write()' và 'p.stdout.read()'. Tuy nhiên, chúng có thể chặn, vì vậy bạn sử dụng 'select()' trên unix hoặc các luồng trên cửa sổ để tránh bị chặn Kiểm tra những gì '.communicate()' thực hiện bằng cách kiểm tra mã nguồn 'subprocess.py' của bạn. Hãy kiểm tra câu trả lời của tôi để biết thêm chi tiết. – nosklo

+0

Đó không phải là câu hỏi của tôi, tôi chỉ cố gắng tạo ra thứ gì đó mà SO có thể thấy hữu ích cho việc sửa mã của anh ấy. Điều đó và tôi đã thực sự tò mò, khi tôi đang làm việc trên một cái gì đó mơ hồ tương tự trong PHP cuối tuần trước và tôi muốn biết làm thế nào cuộc sống tốt hơn một nửa. – Anthony

0

comunicate sẽ chỉ chạy một lần và sau đó sẽ đóng đường ống, do đó nếu bạn muốn gửi một số lệnh để nó, bạn cần phải gửi một sau khi khác trong cùng chuỗi.

Dưới đây là một ví dụ mà làm việc cho tôi sau khi một số điều tra, cố gắng đề, subprocess32, stdin.write, stdout.read vv vv Những thông tin này không có trong các thông tin tham khảo python chính thức cho giao tiếp: https://docs.python.org/2/library/subprocess.html

Các chỉ nơi tôi tìm thấy điều này ở đây: Python subprocess communicate kills my process

Trong mọi trường hợp ở đây là mã, đơn giản, không có chủ đề, không có subprocess32, hoạt động trên linux và windows. Có, bạn phải biết bao nhiêu lần để gửi lệnh cho quá trình khác, nhưng nói chung bạn biết điều đó. Ngày đầu này, bạn có thể thêm chủ đề, PT, vỏ = Đúng hay bất cứ điều gì khác mà bạn có thể muốn nhưng đây là trường hợp đơn giản nhất:

def do_commands(self, cmd, parms): 
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) 

    # wait for the process to terminate 
    out, err = process.communicate(cmd_parms) 
    errcode = process.returncode 

    return errcode, out, err 

Vì vậy, ví dụ nếu bạn muốn gửi nhiều kí tự xuống dòng (\ n) để ứng dụng được gọi và thông số ở giữa (tương tác với bạn), bạn sẽ gọi nó như sau:

cmd_parms = "\n\n\n\n\nparm\n\n" 

errcode, out, err = do_commands(command, cmd_parms) 
Các vấn đề liên quan