Tóm tắt điều hành [hoặc phiên bản "TL; DR"]: Thật dễ dàng khi có nhiều nhất một subprocess.PIPE
, nếu không thì nó khó.
Có thể là thời gian để giải thích một chút về cách subprocess.Popen
làm điều của nó.
.
Hàm Popen
cần phải đối phó với các luồng I/O từ 0 đến ba, hơi đồng thời. Chúng được ký hiệu là stdin
,
for line in proc.stdout:
0 và for line in proc.stdout:
1 như bình thường.Bạn có thể cung cấp:
2, chỉ ra rằng bạn không muốn chuyển hướng luồng. Nó sẽ kế thừa những thứ này như bình thường. Lưu ý rằng trên các hệ thống POSIX, ít nhất, điều này không có nghĩa là nó sẽ sử dụngfor line in proc.stdout:
3 của Python, chỉ là stdout thực tế của Python; Xem bản demo ở cuối.for line in proc.stdout:
- Giá trị
4. Đây là một mô tả tệp "thô" [ít nhất là trong POSIX]. .for line in proc.stdout:
- Một luồng thực sự, bất kỳ đối tượng nào có phương thức
8.for line in proc.stdout:
Popen
sẽ tìm thấy bộ mô tả cho luồng đó, sử dụng
0, và sau đó tiến hành như giá trịproc = subprocess.Popen[cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT]
4.for line in proc.stdout:
subprocess.PIPE
, chỉ ra rằng Python nên tạo ra một đường ống.
3 [chỉ dành choproc = subprocess.Popen[cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT]
1]: Nói với Python sử dụng cùng một mô tả như đối vớifor line in proc.stdout:
0. Điều này chỉ có ý nghĩa nếu bạn cung cấp giá trị [không phải -____ 12] chofor line in proc.stdout:
0, và thậm chí sau đó, nó chỉ cần nếu bạn đặtfor line in proc.stdout:
8. [Nếu không, bạn chỉ có thể cung cấp cùng một đối số mà bạn đã cung cấp cho ____10, ví dụ:proc = subprocess.Popen[cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT]
0.]def communicate[self, input=None]: ... # Optimization: If we are only using one pipe, or no pipe at # all, using select[] or threads is unnecessary. if [self.stdin, self.stdout, self.stderr].count[None] >= 2:
Các trường hợp dễ nhất [không có đường ống]
Nếu bạn chuyển hướng không có gì [để lại cả ba là giá trị
for line in proc.stdout:
2 mặc định hoặc cung cấp rõ ràng for line in proc.stdout:
2], def communicate[self, input=None]:
...
# Optimization: If we are only using one pipe, or no pipe at
# all, using select[] or threads is unnecessary.
if [self.stdin, self.stdout, self.stderr].count[None] >= 2:
3 sẽ khá dễ dàng. Nó chỉ cần quay ra khỏi quy trình con và để nó chạy. Hoặc, nếu bạn chuyển hướng đến một người không phải là -____ 15, một for line in proc.stdout:
4 hoặc một luồng ____ ____ 36, nó vẫn dễ dàng, vì hệ điều hành thực hiện tất cả các công việc. Python chỉ cần xoay chuyển quy trình con, kết nối stdin, stdout và/hoặc stderr của nó với các mô tả tệp được cung cấp.Trường hợp vẫn còn dễ dàng: một ống
Nếu bạn chỉ chuyển đổi một luồng,
def communicate[self, input=None]:
...
# Optimization: If we are only using one pipe, or no pipe at
# all, using select[] or threads is unnecessary.
if [self.stdin, self.stdout, self.stderr].count[None] >= 2:
3 vẫn có những thứ khá dễ dàng. Hãy chọn một luồng một lúc và xem.Giả sử bạn muốn cung cấp một số stdin
, nhưng hãy để ____10 và
for line in proc.stdout:
1 không được kiểm tra hoặc đi đến một bộ mô tả tập tin. Khi quá trình cha mẹ, chương trình Python của bạn chỉ cần sử dụng from cStringIO import StringIO
import os
import subprocess
import sys
def show1[]:
print 'start show1'
save = sys.stdout
sys.stdout = StringIO[]
print 'sys.stdout being buffered'
proc = subprocess.Popen[['echo', 'hello']]
proc.wait[]
in_stdout = sys.stdout.getvalue[]
sys.stdout = save
print 'in buffer:', in_stdout
def show2[]:
print 'start show2'
save = sys.stdout
sys.stdout = open[os.devnull, 'w']
print 'after redirect sys.stdout'
proc = subprocess.Popen[['echo', 'hello']]
proc.wait[]
sys.stdout = save
show1[]
show2[]
1 để gửi dữ liệu xuống đường ống. Bạn có thể tự làm điều này, ví dụ:proc = subprocess.Popen[cmd, stdin=subprocess.PIPE]
proc.stdin.write['here, have some data\n'] # etc
Hoặc bạn có thể chuyển dữ liệu STDIN đến
from cStringIO import StringIO
import os
import subprocess
import sys
def show1[]:
print 'start show1'
save = sys.stdout
sys.stdout = StringIO[]
print 'sys.stdout being buffered'
proc = subprocess.Popen[['echo', 'hello']]
proc.wait[]
in_stdout = sys.stdout.getvalue[]
sys.stdout = save
print 'in buffer:', in_stdout
def show2[]:
print 'start show2'
save = sys.stdout
sys.stdout = open[os.devnull, 'w']
print 'after redirect sys.stdout'
proc = subprocess.Popen[['echo', 'hello']]
proc.wait[]
sys.stdout = save
show1[]
show2[]
2, sau đó from cStringIO import StringIO
import os
import subprocess
import sys
def show1[]:
print 'start show1'
save = sys.stdout
sys.stdout = StringIO[]
print 'sys.stdout being buffered'
proc = subprocess.Popen[['echo', 'hello']]
proc.wait[]
in_stdout = sys.stdout.getvalue[]
sys.stdout = save
print 'in buffer:', in_stdout
def show2[]:
print 'start show2'
save = sys.stdout
sys.stdout = open[os.devnull, 'w']
print 'after redirect sys.stdout'
proc = subprocess.Popen[['echo', 'hello']]
proc.wait[]
sys.stdout = save
show1[]
show2[]
3 hiển thị ở trên. Không có đầu ra nào trở lại nên from cStringIO import StringIO
import os
import subprocess
import sys
def show1[]:
print 'start show1'
save = sys.stdout
sys.stdout = StringIO[]
print 'sys.stdout being buffered'
proc = subprocess.Popen[['echo', 'hello']]
proc.wait[]
in_stdout = sys.stdout.getvalue[]
sys.stdout = save
print 'in buffer:', in_stdout
def show2[]:
print 'start show2'
save = sys.stdout
sys.stdout = open[os.devnull, 'w']
print 'after redirect sys.stdout'
proc = subprocess.Popen[['echo', 'hello']]
proc.wait[]
sys.stdout = save
show1[]
show2[]
4 chỉ có một công việc thực sự khác: nó cũng đóng đường ống cho bạn. .Giả sử bạn muốn chụp
for line in proc.stdout:
0 nhưng chỉ để stdin
và for line in proc.stdout:
1 một mình. Một lần nữa, thật dễ dàng: Chỉ cần gọi $ python out.py
start show1
hello
in buffer: sys.stdout being buffered
start show2
hello
0 [hoặc tương đương] cho đến khi không còn đầu ra. Vì $ python out.py
start show1
hello
in buffer: sys.stdout being buffered
start show2
hello
1 là luồng Python I/O bình thường, bạn có thể sử dụng tất cả các cấu trúc thông thường trên đó, như:for line in proc.stdout:
Hoặc, một lần nữa, bạn có thể sử dụng
from cStringIO import StringIO
import os
import subprocess
import sys
def show1[]:
print 'start show1'
save = sys.stdout
sys.stdout = StringIO[]
print 'sys.stdout being buffered'
proc = subprocess.Popen[['echo', 'hello']]
proc.wait[]
in_stdout = sys.stdout.getvalue[]
sys.stdout = save
print 'in buffer:', in_stdout
def show2[]:
print 'start show2'
save = sys.stdout
sys.stdout = open[os.devnull, 'w']
print 'after redirect sys.stdout'
proc = subprocess.Popen[['echo', 'hello']]
proc.wait[]
sys.stdout = save
show1[]
show2[]
2, đơn giản là $ python out.py
start show1
hello
in buffer: sys.stdout being buffered
start show2
hello
3 cho bạn.Nếu bạn chỉ muốn chụp
for line in proc.stdout:
1, nó hoạt động giống như với for line in proc.stdout:
0.Có một mẹo nữa trước khi mọi thứ trở nên khó khăn. Giả sử bạn muốn chụp
for line in proc.stdout:
0, và cũng nắm bắt for line in proc.stdout:
1 nhưng trên cùng một đường ống với stdout:proc = subprocess.Popen[cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT]
Trong trường hợp này,
$ python out.py
start show1
hello
in buffer: sys.stdout being buffered
start show2
hello
8 "Cheat"! Chà, nó phải làm điều này, vì vậy nó không thực sự gian lận: nó bắt đầu quá trình phụ với cả stdout và stderr của nó được hướng vào bộ mô tả ống [đơn] cung cấp lại quy trình cha mẹ [Python] của nó. Về phía cha mẹ, một lần nữa chỉ có một bộ mô tả đường ống duy nhất để đọc đầu ra. Tất cả đầu ra "stderr" hiển thị trong $ python out.py
start show1
hello
in buffer: sys.stdout being buffered
start show2
hello
9 và nếu bạn gọi from cStringIO import StringIO
import os
import subprocess
import sys
def show1[]:
print 'start show1'
save = sys.stdout
sys.stdout = StringIO[]
print 'sys.stdout being buffered'
proc = subprocess.Popen[['echo', 'hello']]
proc.wait[]
in_stdout = sys.stdout.getvalue[]
sys.stdout = save
print 'in buffer:', in_stdout
def show2[]:
print 'start show2'
save = sys.stdout
sys.stdout = open[os.devnull, 'w']
print 'after redirect sys.stdout'
proc = subprocess.Popen[['echo', 'hello']]
proc.wait[]
sys.stdout = save
show1[]
show2[]
2, kết quả STDERR [giá trị thứ hai trong tuple] sẽ là for line in proc.stdout:
2, không phải là một chuỗi.Các trường hợp cứng: Hai hoặc nhiều đường ống
Tất cả các vấn đề xảy ra khi bạn muốn sử dụng ít nhất hai đường ống. Trên thực tế, mã
$ python out.py
start show1
hello
in buffer: sys.stdout being buffered
start show2
hello
8 có bit này:def communicate[self, input=None]:
...
# Optimization: If we are only using one pipe, or no pipe at
# all, using select[] or threads is unnecessary.
if [self.stdin, self.stdout, self.stderr].count[None] >= 2:
Nhưng, than ôi, ở đây chúng tôi đã thực hiện ít nhất hai, và có thể ba, các đường ống khác nhau, vì vậy subprocess.PIPE
3 trả về 1 hoặc 0. Chúng ta phải làm mọi thứ một cách khó khăn.
Trên Windows, điều này sử dụng subprocess.PIPE
4 để tích lũy kết quả cho subprocess.PIPE
5 và subprocess.PIPE
6 và có luồng cha mẹ cung cấp dữ liệu đầu vào subprocess.PIPE
7 [và sau đó đóng đường ống].
Trên POSIX, điều này sử dụng subprocess.PIPE
8 nếu có, nếu không subprocess.PIPE
9, để tích lũy đầu ra và cung cấp đầu vào stdin. Tất cả điều này chạy trong quy trình/chủ đề cha mẹ [đơn].
Chủ đề hoặc cuộc thăm dò/chọn là cần thiết ở đây để tránh bế tắc. Ví dụ, giả sử rằng chúng tôi đã chuyển hướng cả ba luồng đến ba đường ống riêng biệt. Giả sử thêm rằng có một giới hạn nhỏ về số lượng dữ liệu có thể được nhét vào đường ống trước khi quá trình viết bị đình chỉ, chờ quá trình đọc "làm sạch" đường ống từ đầu kia. Hãy đặt giới hạn nhỏ đó cho một byte duy nhất, chỉ để minh họa. [Đây thực sự là cách mọi thứ hoạt động, ngoại trừ giới hạn đó lớn hơn nhiều so với một byte.]
Nếu quá trình cha mẹ [python] cố gắng viết một số byte, say, subprocess.Popen
0to subprocess.Popen
1, byte đầu tiên đi vào và sau đó lần thứ hai khiến quá trình Python đình chỉ, chờ quá trình phụ đọc byte đầu tiên, làm trống đường ống.
Trong khi đó, giả sử quá trình phụ quyết định in một "Xin chào! Đừng hoảng sợ!" Lời chào. subprocess.Popen
2 đi vào đường ống stdout của nó, nhưng subprocess.Popen
3 khiến nó bị đình chỉ, chờ cha mẹ của nó đọc subprocess.Popen
2 đó, làm trống ống stdout.
Bây giờ chúng tôi bị mắc kẹt: Quá trình Python đang ngủ, chờ nói xong "đi", và quá trình phụ cũng đang ngủ, chờ nói xong "Xin chào! Đừng hoảng sợ!".
Mã subprocess.Popen
tránh được vấn đề này với luồng hoặc chọn/thăm dò ý kiến. Khi byte có thể đi qua các đường ống, chúng đi. Khi chúng không thể, chỉ có một chủ đề [không phải toàn bộ quá trình] phải ngủ hoặc trong trường hợp chọn/thăm dò ý kiến, quá trình Python chờ đồng thời "có thể viết" hoặc "dữ liệu có sẵn", ghi vào stdin của quy trình Chỉ khi có phòng, và chỉ đọc stdout và/hoặc stderr của nó chỉ khi dữ liệu đã sẵn sàng. Mã
from cStringIO import StringIO
import os
import subprocess
import sys
def show1[]:
print 'start show1'
save = sys.stdout
sys.stdout = StringIO[]
print 'sys.stdout being buffered'
proc = subprocess.Popen[['echo', 'hello']]
proc.wait[]
in_stdout = sys.stdout.getvalue[]
sys.stdout = save
print 'in buffer:', in_stdout
def show2[]:
print 'start show2'
save = sys.stdout
sys.stdout = open[os.devnull, 'w']
print 'after redirect sys.stdout'
proc = subprocess.Popen[['echo', 'hello']]
proc.wait[]
sys.stdout = save
show1[]
show2[]
2 [thực tế là subprocess.Popen
7 trong đó các trường hợp có lông được xử lý] trả về một khi tất cả dữ liệu STDIN [nếu có] đã được gửi và tất cả dữ liệu stdout và/hoặc stderr đã được tích lũy.Nếu bạn muốn đọc cả
for line in proc.stdout:
0 và for line in proc.stdout:
1 trên hai đường ống khác nhau [bất kể chuyển hướng stdin
], bạn cũng sẽ cần tránh bế tắc. Kịch bản bế tắc ở đây là khác nhau, nó xảy ra khi quy trình con viết một cái gì đó dài đến for line in proc.stdout:
1 trong khi bạn đang lấy dữ liệu từ for line in proc.stdout:
0 hoặc ngược lại, nhưng nó vẫn ở đó.Bản demo
Tôi đã hứa sẽ chứng minh rằng, không được tái định hướng, Python
$ python out.py
start show1
hello
in buffer: sys.stdout being buffered
start show2
hello
8es viết cho stdout cơ bản, không phải for line in proc.stdout:
3. Vì vậy, đây là một số mã:from cStringIO import StringIO
import os
import subprocess
import sys
def show1[]:
print 'start show1'
save = sys.stdout
sys.stdout = StringIO[]
print 'sys.stdout being buffered'
proc = subprocess.Popen[['echo', 'hello']]
proc.wait[]
in_stdout = sys.stdout.getvalue[]
sys.stdout = save
print 'in buffer:', in_stdout
def show2[]:
print 'start show2'
save = sys.stdout
sys.stdout = open[os.devnull, 'w']
print 'after redirect sys.stdout'
proc = subprocess.Popen[['echo', 'hello']]
proc.wait[]
sys.stdout = save
show1[]
show2[]
Khi chạy:
$ python out.py
start show1
hello
in buffer: sys.stdout being buffered
start show2
hello
Lưu ý rằng thói quen đầu tiên sẽ thất bại nếu bạn thêm Popen
5, vì đối tượng Popen
6 không có
for line in proc.stdout:
8. Thứ hai sẽ bỏ qua Popen
8 nếu bạn thêm Popen
5 vì for line in proc.stdout:
3 đã được chuyển hướng đến stdin
1..