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.

Author: Peter Mortensen, 2011-12-12

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.

 92
Author: jro,
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.
 6
Author: eaydin,
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'
 -2
Author: ,
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:48:59