Jeśli wykonam następujące czynności:
import subprocess
from cStringIO import StringIO
subprocess.Popen(['grep','f'],stdout=subprocess.PIPE,stdin=StringIO('one\ntwo\nthree\nfour\nfive\nsix\n')).communicate()[0]
Dostaję:
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "/build/toolchain/mac32/python-2.4.3/lib/python2.4/subprocess.py", line 533, in __init__
(p2cread, p2cwrite,
File "/build/toolchain/mac32/python-2.4.3/lib/python2.4/subprocess.py", line 830, in _get_handles
p2cread = stdin.fileno()
AttributeError: 'cStringIO.StringI' object has no attribute 'fileno'
Najwyraźniej obiekt cStringIO.StringIO nie kwakuje wystarczająco blisko kaczki pliku, aby pasował do podprocesu.Popen. Jak obejść ten problem?
python
subprocess
stdin
Daryl Spitzer
źródło
źródło
call(['ls', '-1'], shell=True)
jest niepoprawny. Zamiast tego polecam przeczytanie typowych pytań z opisu znacznika podprocesu . W szczególności: Dlaczego podproces.Popen nie działa, gdy argument jest sekwencją? wyjaśnia, dlaczegocall(['ls', '-1'], shell=True)
jest źle. Pamiętam, jak zostawiam komentarze pod postem na blogu, ale z jakiegoś powodu nie widzę ich teraz.subprocess.run
patrz stackoverflow.com/questions/48752152/…Odpowiedzi:
Popen.communicate()
dokumentacja:Twój przykład można zapisać następująco:
W bieżącej wersji Python 3 można użyć
subprocess.run
, aby przekazać dane wejściowe jako ciąg znaków do polecenia zewnętrznego i uzyskać jego status wyjścia, a dane wyjściowe jako ciąg znaków z powrotem w jednym wywołaniu:źródło
Zrozumiałem to obejście:
Czy jest lepszy?
źródło
stdin.write()
korzystanie nie jestp.communicate()
zalecane , należy je stosować. Zobacz moją odpowiedź.communicate
zamkną potok i pozwolą procesowi zakończyć się z wdziękiem.read()
zrobić, tak jakcommunicate()
to bez argumentów).Jestem trochę zaskoczony, że nikt nie zasugerował stworzenia fajki, co jest moim zdaniem najprostszym sposobem na przekazanie łańcucha do standardowego podprocesu:
źródło
os
isubprocess
dokumentacja zgadzają się, że powinieneś preferować tę drugą opcję. Jest to starsze rozwiązanie, które ma (nieco mniej zwięzłe) standardowe zamienniki; przyjęta odpowiedź przytacza stosowną dokumentację.Jest piękne rozwiązanie, jeśli używasz języka Python 3.4 lub nowszego. Użyj
input
argumentu zamiaststdin
argumentu, który akceptuje argument bajtów:Działa to dla
check_output
arun
, ale niecall
lubcheck_call
z jakiegoś powodu.źródło
check_output
powinienem miećinput
argument, ale niecall
.check_call
ale działarun
. Działa również z ciągiem input =, o ile przekazujesz argument kodowania zgodnie z dokumentacją.Korzystam z Python3 i odkryłem, że musisz zakodować swój ciąg, zanim będziesz mógł przekazać go do standardowego wejścia:
źródło
b'something'
.). Zwróci również błędy i zniknie jako bajty. Jeśli chcesz tego uniknąć, możesz przejśćuniversal_newlines=True
doPopen
. Następnie zaakceptuje dane wejściowe jako str i zwróci również err / out jako str.universal_newlines=True
przekonwertuje również twoje nowe wiersze, aby pasowały do twojego systemuObawiam się że nie. Potok jest koncepcją niskiego poziomu systemu operacyjnego, więc absolutnie wymaga obiektu pliku reprezentowanego przez deskryptor pliku na poziomie systemu operacyjnego. Twoje obejście jest właściwe.
źródło
źródło
Uważaj, ponieważ
Popen.communicate(input=s)
może być kłopotliwy, jeślis
jest zbyt duży, ponieważ najwyraźniej proces nadrzędny buforuje go przed rozwidleniem podprocesu potomnego, co oznacza, że potrzebuje „dwukrotnie więcej” zużytej pamięci w tym momencie (przynajmniej zgodnie z wyjaśnieniem „pod maską” i powiązaną dokumentację tutaj ). W moim szczególnym przypadkus
był to generator, który najpierw został w pełni rozwinięty, a dopiero potem napisany w nim,stdin
aby proces nadrzędny był ogromny tuż przed spawnowaniem dziecka i nie pozostawiono pamięci, aby go rozwidlić:File "/opt/local/stow/python-2.7.2/lib/python2.7/subprocess.py", line 1130, in _execute_child self.pid = os.fork() OSError: [Errno 12] Cannot allocate memory
źródło
źródło
shell=True
jest to tak często używane bez uzasadnionego powodu i jest to popularne pytanie, pozwolę sobie zauważyć, że istnieje wiele sytuacji, w którychPopen(['cmd', 'with', 'args'])
zdecydowanie lepiej jestPopen('cmd with args', shell=True)
mieć powłokę dzielącą polecenia i argumenty na tokeny, ale w żaden inny sposób nie dostarczając niczego przydatne, jednocześnie dodając znaczną złożoność, a tym samym atakując powierzchnię.źródło
W Pythonie 3.7+ wykonaj następujące czynności:
i prawdopodobnie będziesz chciał dodać,
capture_output=True
aby uzyskać wynik uruchomienia polecenia jako ciąg.W starszych wersjach Pythona, wymienić
text=True
zuniversal_newlines=True
:źródło