Jak napisać na stdin Podprocesu Pythona?
Próbuję napisać skrypt Pythona, który uruchamia podproces i zapisuje do podprocesu stdin. Chciałbym również być w stanie określić działanie, które należy podjąć w przypadku awarii podprocesu.
Proces, który próbuję uruchomić, to program o nazwie nuke
, który ma własną wbudowaną wersję Pythona, do której chciałbym móc wysyłać polecenia, a następnie kazać mu zamknąć po wykonaniu poleceń. Do tej pory doszedłem do wniosku, że jeśli uruchomię Pythona w wierszu polecenia lubię, a następnie uruchamiam nuke
jako podproces, a następnie mogę wpisywać polecenia do nuke
, ale chciałbym móc umieścić to wszystko w skrypcie, aby główny program Pythona mógł uruchomić nuke
, a następnie zapisać do jego standardowego wejścia (a więc do jego wbudowanej wersji Pythona) i powiedzieć mu, aby robił niesamowite rzeczy, więc napisałem skrypt, który zaczyna się nuke
tak:
subprocess.call(["C:/Program Files/Nuke6.3v5/Nuke6.3", "-t", "E:/NukeTest/test.nk"])
Wtedy nic się nie dzieje, ponieważ nuke
czeka na wejście użytkownika. Jak mam teraz pisać na standardowe wejście?
I ' m robienie tego, ponieważ używam wtyczki z nuke
, która powoduje, że zawiesza się sporadycznie podczas renderowania wielu klatek. Więc chciałbym, aby ten skrypt mógł uruchomić nuke
, powiedzieć mu, aby coś zrobił, a następnie, jeśli się zawiesi, spróbuj ponownie. Więc jeśli jest sposób, aby złapać wypadek i nadal być w porządku, to byłoby świetnie.
3 answers
Może lepiej użyć communicate
:
from subprocess import Popen, PIPE, STDOUT
p = Popen(['myapp'], stdout=PIPE, stdin=PIPE, stderr=PIPE)
stdout_data = p.communicate(input='data_to_write')[0]
"lepiej", z powodu tego ostrzeżenia:
Użyj funkcji communicate() zamiast .stdin.napisz, .stdout.Przeczytaj lub .stderr.przeczytaj, aby uniknąć blokad spowodowanych przez inne bufory rurowe systemu operacyjnego wypełniające się i blokujące proces potomny.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-12 13:52:56
Aby wyjaśnić niektóre punkty:
Ponieważ jro ma wspomniane, właściwym sposobem jest użycie subprocess.communicate
.
Jednak podczas podawania stdin
za pomocą subprocess.communicate
z input
, musisz zainicjować podproces za pomocą stdin=subprocess.PIPE
zgodnie z docs.
Zauważ, że jeśli chcesz wysłać dane na stdin procesu, musisz utworzyć obiekt Popen za pomocą stdin=PIPE. Podobnie, aby uzyskać coś innego niż brak w krotce wyniku, musisz podać stdout=PIPE i / lub stderr=PIPE też.
Również qed wspomniał w komentarzach, że w Pythonie 3.4 musisz zakodować łańcuch znaków, co oznacza, że musisz przekazać bajty do input
, a nie string
. To nie do końca prawda. Zgodnie z dokumentami, jeśli strumienie były otwierane w trybie tekstowym, wejście powinno być ciągiem znaków (Źródło to ta sama strona).
Jeśli strumienie były otwierane w trybie tekstowym, wejście musi być ciągiem znaków. W przeciwnym razie muszą to być bajty.
Więc jeśli strumienie nie były otwierane jawnie w trybie tekstowym, wtedy powinno działać coś takiego jak poniżej:
import subprocess
command = ['myapp', '--arg1', 'value_for_arg1']
p = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output = p.communicate(input='some data'.encode())[0]
Zostawiłem stderr
wartość powyżej celowo jako STDOUT
jako przykład.
To powiedziawszy, czasami możesz chcieć uzyskać wynik innego procesu, zamiast budować go od zera. Załóżmy, że chcesz uruchomić odpowiednik echo -n 'CATCH\nme' | grep -i catch | wc -m
. Powinno to normalnie zwracać znaki liczbowe w 'CATCH' plus znak nowej linii, co daje 6. Punktem echa jest aby przesłać dane CATCH\nme
do grepa. Możemy więc przesyłać dane do grepa za pomocą stdin w łańcuchu podprocesów Pythona jako zmienną, a następnie przekazać stdout jako rurę do procesu wc
' stdin (w międzyczasie pozbądź się dodatkowego znaku nowej linii):
import subprocess
what_to_catch = 'catch'
what_to_feed = 'CATCH\nme'
# We create the first subprocess, note that we need stdin=PIPE and stdout=PIPE
p1 = subprocess.Popen(['grep', '-i', what_to_catch], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
# We immediately run the first subprocess and get the result
# Note that we encode the data, otherwise we'd get a TypeError
p1_out = p1.communicate(input=what_to_feed.encode())[0]
# Well the result includes an '\n' at the end,
# if we want to get rid of it in a VERY hacky way
p1_out = p1_out.decode().strip().encode()
# We create the second subprocess, note that we need stdin=PIPE
p2 = subprocess.Popen(['wc', '-m'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
# We run the second subprocess feeding it with the first subprocess' output.
# We decode the output to convert to a string
# We still have a '\n', so we strip that out
output = p2.communicate(input=p1_out)[0].decode().strip()
Jest to nieco inne rozwiązanie niż odpowiedź tutaj , gdzie przesyłasz dwa procesy bezpośrednio bez dodawania danych bezpośrednio w Pythonie.
Mam nadzieję, że to komuś pomoże.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-08 22:23:18
Możesz podać obiekt podobny do pliku do stdin
argumentu subprocess.call()
.
Stosuje się tutaj dokumentację dla obiektu Popen
.
Aby przechwycić wyjście, powinieneś użyć subprocess.check_output()
, która pobiera podobne argumenty. Z dokumentacji:
>>> subprocess.check_output(
... "ls non_existent_file; exit 0",
... stderr=subprocess.STDOUT,
... shell=True)
'ls: non_existent_file: No such file or directory\n'