Jak przekierować wyjście za pomocą podprocesu w Pythonie?

97

Co robię w linii poleceń:

cat file1 file2 file3 > myfile

Co chcę zrobić z Pythonem:

import subprocess, shlex
my_cmd = 'cat file1 file2 file3 > myfile'
args = shlex.split(my_cmd)
subprocess.call(args) # spits the output in the window i call my python program
catatemypythoncode
źródło
Wykonanie takiego polecenia w podprocesie nie dałoby żadnych wyników. Może chcesz go uruchomić bez przekierowania > myfile wyjścia z cat plik1 plik2 plik3 do Pythona?
PoltoS
@PoltoS Chcę dołączyć do niektórych plików, a następnie przetworzyć plik wynikowy. Myślałem, że używanie kota to najłatwiejsza alternatywa. Czy jest na to lepszy / pythonowy sposób?
catatemypythoncode
os.sendfile()-based rozwiązanie jest możliwe, zobacz Reproduce the unix cat command in python
jfs
1
Myślę, że przekierowanie wyjścia ('>' lub '>>') nie działa w podprocesie.Popen (przynajmniej w Pythonie 2.7) (w trybie powłoki = True) W tym przykładzie, jak wskazują inni, można obejść to bez użycia przekierowania, ale w innych przypadkach przekierowanie jest przydatne. Jeśli przekierowanie lub potoki nie są obsługiwane w podprocesie, należy udokumentować wartość otwartą (i / lub nie należy używać funkcji os.system (), dopóki nie zostanie to naprawione)
Ribo

Odpowiedzi:

20

UPDATE: os.system jest odradzany, chociaż nadal dostępny w Pythonie 3.


Zastosowanie os.system:

os.system(my_cmd)

Jeśli naprawdę chcesz użyć podprocesu, oto rozwiązanie (głównie zaczerpnięte z dokumentacji podprocesu):

p = subprocess.Popen(my_cmd, shell=True)
os.waitpid(p.pid, 0)

OTOH, możesz całkowicie uniknąć wywołań systemowych:

import shutil

with open('myfile', 'w') as outfile:
    for infile in ('file1', 'file2', 'file3'):
        shutil.copyfileobj(open(infile), outfile)
Marcelo Cantos
źródło
1
Działa, ale pozwólcie, że zapytam was: jaki jest sens biblioteki podprocesów, jeśli os.system już wykonuje zadanie? Mam wrażenie, że zamiast tego powinienem użyć podprocesu, ponieważ jest to biblioteka dedykowana temu zadaniu, chociaż ponieważ robię to tylko dla siebie, tym razem będę w porządku, używając os.system.
catatemypythoncode
Biblioteka podprocesów jest znacznie bardziej elastyczna os.systemi umożliwia os.systemprecyzyjne modelowanie , ale jest też bardziej złożona w obsłudze.
Marcelo Cantos
13
os.systemprzyszedł wcześniej subprocess. Pierwszy z nich to starszy interfejs API, który drugi zamierza zastąpić.
Święty Mikołaj
5
@catatemypythoncode: nie należy używać os.system()ani shell=True. Aby przekierować wyjście podprocesu, użyj stdoutparametru pokazanego w odpowiedzi Ryana Thompsona . Chociaż catw twoim przypadku nie potrzebujesz subprocess ( ), możesz łączyć pliki przy użyciu czystego Pythona.
jfs
4
OTOH = Z drugiej strony
Cephlin
272

W Pythonie 3.5+, aby przekierować dane wyjściowe, po prostu przekaż uchwyt otwartego pliku dla stdoutargumentu do subprocess.run:

# Use a list of args instead of a string
input_files = ['file1', 'file2', 'file3']
my_cmd = ['cat'] + input_files
with open('myfile', "w") as outfile:
    subprocess.run(my_cmd, stdout=outfile)

Jak inni zauważyli, użycie zewnętrznego polecenia, takiego jak catw tym celu, jest całkowicie obce.

Ryan C. Thompson
źródło
9
To powinna być odpowiedź na ogólne pytanie o orurowanie podczas korzystania z powłoki z Pythona
Kaushik Ghose
47
To jest poprawna odpowiedź, a nie ta oznaczona jako poprawna.
Justin Blake,
7
Do użytku w Pythonie 3.5+, subprocess.run(my_cmd, stdout=outfile)który zastępujesubprocess.call(...)
Austin Yates
1
Warto zauważyć, że nie działa to z niestandardowymi obiektami plików, jeśli nie mają one pola fileno (jeśli nie są prawdziwym plikiem).
Eliezer Miron
1
Ponieważ Python <3.5 jest obecnie przestarzały, zaktualizowałem odpowiedź o Twój komentarz, @AustinYates.
Greg Dubicki
5

@PoltoS Chcę dołączyć do niektórych plików, a następnie przetworzyć plik wynikowy. Myślałem, że używanie kota to najłatwiejsza alternatywa. Czy jest na to lepszy / pythonowy sposób?

Oczywiście:

with open('myfile', 'w') as outfile:
    for infilename in ['file1', 'file2', 'file3']:
        with open(infilename) as infile:
            outfile.write(infile.read())
SingleNegationElimination
źródło
1
size = 'ffprobe -v error -show_entries format=size -of default=noprint_wrappers=1:nokey=1 dump.mp4 > file'
proc = subprocess.Popen(shlex.split(size), shell=True)
time.sleep(1)
proc.terminate() #proc.kill() modify it by a suggestion
size = ""
with open('file', 'r') as infile:
    for line in infile.readlines():
        size += line.strip()

print(size)
os.remove('file')

Kiedy używasz podprocesu , proces musi zostać zabity.To jest przykład.Jeśli nie zabijesz procesu, plik będzie pusty i nie będziesz mógł nic odczytać.Może działać w systemie Windows.Nie mogę się upewnić, że może działa na Uniksie.

wyx
źródło
1
Jest to zły przykład kodu (nie będzie działać na systemie Unix, to pokazuje złe praktyki for line in .readlines():, s +=) i proc.kill()może prowadzić do utraty danych w ogóle (to nie pozwala, aby zakończyć podproces wdzięcznie (na Unix) - unflushed zawartość jest tracona ). W każdym razie uwaga o buforowaniu jest bardziej odpowiednia jako komentarz.
jfs
Uruchamiam go w systemie Windows jest OK (ponieważ zabicie jest równe zakończeniu w systemie Windows). Na Unixie możesz użyć proc.terminate (). @ JF Sebastian Nie mam systemu Unix na moim komputerze.
wyx
Jeśli jesteś na systemie Windows, a następnie upuścić shlex.split(), spadek shell=True, spadek >file, spadek open(), etc i wykorzystania stdout=PIPE, Timer(1, proc.terminate).start(); output = proc.communicate()[0]zamiast. Oto pełny przykład . Więcej rozwiązań: zatrzymać odczytywanie wyników procesu w Pythonie bez zawieszania się? Uwaga: w pytaniu nie ma wymogu ręcznego zakończenia procesu potomnego - możesz rozwiązać inne problemy, np. Proces może zachowywać się inaczej, jeśli jego standardowym wyjściem jest tty, ale nie dotyczy tematu.
jfs
0

Ciekawym przypadkiem byłoby zaktualizowanie pliku przez dołączenie do niego podobnego pliku. Wtedy nie trzeba by było w trakcie tworzenia nowego pliku. Jest to szczególnie przydatne w przypadku konieczności uzupełnienia dużego pliku. Oto jedna możliwość użycia wiersza poleceń teminal bezpośrednio z Pythona.

import subprocess32 as sub

with open("A.csv","a") as f:
    f.flush()
    sub.Popen(["cat","temp.csv"],stdout=f)
DJJ
źródło