Uruchamianie polecenia powłoki i przechwytywanie danych wyjściowych

905

Chcę napisać funkcję, która wykona polecenie powłoki i zwróci jego wynik jako ciąg , bez względu na to, czy jest to komunikat o błędzie czy sukcesie. Chcę tylko uzyskać ten sam wynik, który uzyskałbym za pomocą wiersza poleceń.

Jaki byłby przykład kodu, który zrobiłby coś takiego?

Na przykład:

def run_command(cmd):
    # ??????

print run_command('mysqladmin create test -uroot -pmysqladmin12')
# Should output something like:
# mysqladmin: CREATE DATABASE failed; error: 'Can't create database 'test'; database exists'
Srebrne światło
źródło

Odpowiedzi:

1137

Odpowiedź na to pytanie zależy od używanej wersji języka Python. Najprostszym podejściem jest użycie subprocess.check_outputfunkcji:

>>> subprocess.check_output(['ls', '-l'])
b'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

check_outputuruchamia pojedynczy program, który przyjmuje tylko argumenty jako dane wejściowe. 1 Zwraca wynik dokładnie tak, jak wydrukowano stdout. Jeśli chcesz zapisać dane wejściowe stdin, przejdź do sekcji runlub Popen. Jeśli chcesz wykonywać złożone polecenia powłoki, zobacz uwagę na shell=Truekońcu tej odpowiedzi.

Ta check_outputfunkcja działa na prawie wszystkich wersjach Pythona, które są nadal w powszechnym użyciu (2.7+). 2 Jednak w przypadku nowszych wersji nie jest to już zalecane podejście.

Nowoczesne wersje Python (3.5 lub nowszy): run

Jeśli używasz języka Python 3.5 lub nowszego i nie potrzebujesz kompatybilności wstecznej , nowa runfunkcja jest zalecana. Zapewnia bardzo ogólny interfejs API wysokiego poziomu dla subprocessmodułu. Aby przechwycić dane wyjściowe programu, przekaż subprocess.PIPEflagę do stdoutargumentu słowa kluczowego. Następnie uzyskaj dostęp do stdoutatrybutu zwróconego CompletedProcessobiektu:

>>> import subprocess
>>> result = subprocess.run(['ls', '-l'], stdout=subprocess.PIPE)
>>> result.stdout
b'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

Zwracana wartość to bytesobiekt, więc jeśli potrzebujesz odpowiedniego ciągu, musisz decodego. Zakładając, że wywoływany proces zwraca łańcuch zakodowany w UTF-8:

>>> result.stdout.decode('utf-8')
'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

Wszystko to można skompresować do jednej linijki:

>>> subprocess.run(['ls', '-l'], stdout=subprocess.PIPE).stdout.decode('utf-8')
'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

Jeśli chcesz przekazać dane wejściowe do procesu stdin, przekaż bytesobiekt do inputargumentu słowa kluczowego:

>>> cmd = ['awk', 'length($0) > 5']
>>> input = 'foo\nfoofoo\n'.encode('utf-8')
>>> result = subprocess.run(cmd, stdout=subprocess.PIPE, input=input)
>>> result.stdout.decode('utf-8')
'foofoo\n'

Błędy można wychwytywać, przekazując stderr=subprocess.PIPE(przechwytywanie do result.stderr) lub stderr=subprocess.STDOUT(przechwytywanie result.stdoutwraz ze zwykłym wyjściem). Gdy bezpieczeństwo nie stanowi problemu, możesz także uruchamiać bardziej złożone polecenia powłoki, przekazując shell=Truezgodnie z opisem w uwagach poniżej.

Dodaje to nieco złożoności w porównaniu do starego sposobu robienia rzeczy. Ale myślę, że jest to opłacalne: teraz możesz zrobić prawie wszystko, co musisz zrobić z runsamą funkcją.

Starsze wersje Pythona (2.7-3.4): check_output

Jeśli używasz starszej wersji Pythona lub potrzebujesz skromnej kompatybilności wstecznej, prawdopodobnie możesz użyć tej check_outputfunkcji w skrócie opisany powyżej. Jest dostępny od wersji Python 2.7.

subprocess.check_output(*popenargs, **kwargs)  

Pobiera te same argumenty co Popen(patrz poniżej) i zwraca ciąg zawierający dane wyjściowe programu. Początek tej odpowiedzi zawiera bardziej szczegółowy przykład użycia. W Pythonie 3.5 i nowszych check_outputjest równoważne wykonywaniu za runpomocą check=Truei stdout=PIPEi zwracaniu tylko stdoutatrybutu.

Możesz przekazać, stderr=subprocess.STDOUTaby upewnić się, że komunikaty o błędach są uwzględnione w zwróconych danych wyjściowych - ale w niektórych wersjach przekazywanie stderr=subprocess.PIPEdo Pythona check_outputmoże powodować zakleszczenia . Gdy bezpieczeństwo nie stanowi problemu, możesz także uruchamiać bardziej złożone polecenia powłoki, przekazując shell=Truezgodnie z opisem w uwagach poniżej.

Jeśli potrzebujesz potokować stderrlub przekazywać dane wejściowe do procesu, check_outputnie będzie to do zadania. PopenW takim przypadku zobacz poniższe przykłady.

Złożone aplikacje i starsze wersje Pythona (2.6 i poniżej): Popen

Jeśli potrzebujesz głębokiej kompatybilności wstecznej lub potrzebujesz bardziej wyrafinowanej funkcjonalności niż check_outputzapewnia, będziesz musiał pracować bezpośrednio z Popenobiektami, które zawierają niskopoziomowy interfejs API dla podprocesów.

PopenKonstruktor przyjmuje zarówno pojedyncze polecenie , bez argumentów lub listy zawierającej polecenie jako jego pierwszej pozycji, a po dowolnej liczbie argumentów każdej jako oddzielny element na liście. shlex.splitmoże pomóc w analizie ciągów znaków w odpowiednio sformatowanych listach. Popenobiekty akceptują również wiele różnych argumentów do zarządzania procesami IO i konfiguracji niskiego poziomu.

Wysyłanie danych wejściowych i przechwytywanie danych wyjściowych communicatejest prawie zawsze preferowaną metodą. Jak w:

output = subprocess.Popen(["mycmd", "myarg"], 
                          stdout=subprocess.PIPE).communicate()[0]

Lub

>>> import subprocess
>>> p = subprocess.Popen(['ls', '-a'], stdout=subprocess.PIPE, 
...                                    stderr=subprocess.PIPE)
>>> out, err = p.communicate()
>>> print out
.
..
foo

Jeśli ustawisz stdin=PIPE, communicatepozwala również na przekazywanie danych do procesu poprzez stdin:

>>> cmd = ['awk', 'length($0) > 5']
>>> p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
...                           stderr=subprocess.PIPE,
...                           stdin=subprocess.PIPE)
>>> out, err = p.communicate('foo\nfoofoo\n')
>>> print out
foofoo

UWAGA Aaron Hall odpowiedź , która wskazuje, że w niektórych systemach, może trzeba ustawić stdout, stderri stdinwszystko PIPE(lub DEVNULL), aby dostać się communicatedo pracy w ogóle.

W niektórych rzadkich przypadkach może być konieczne przechwytywanie danych wyjściowych w czasie rzeczywistym. Odpowiedź Vartec sugeruje drogę naprzód, ale metody inne niż communicatepodatne na impas, jeśli nie są stosowane ostrożnie.

Podobnie jak w przypadku wszystkich powyższych funkcji, gdy bezpieczeństwo nie stanowi problemu, można uruchamiać bardziej złożone polecenia powłoki, przekazując shell=True.

Notatki

1. Uruchamianie poleceń powłoki: shell=Trueargument

Normalnie, każde wywołanie run, check_outputlub Popenkonstruktor wykonuje jeden program . Oznacza to, że nie ma żadnych fajnych rur typu bash. Jeśli chcesz uruchamiać złożone polecenia powłoki, możesz przekazać shell=True, które obsługują wszystkie trzy funkcje.

Takie postępowanie budzi jednak obawy dotyczące bezpieczeństwa . Jeśli robisz coś więcej niż lekkie skrypty, lepiej byłoby wywołać każdy proces osobno i przekazać dane wyjściowe z każdego z nich jako dane wejściowe do następnego, za pośrednictwem

run(cmd, [stdout=etc...], input=other_output)

Lub

Popen(cmd, [stdout=etc...]).communicate(other_output)

Pokusa bezpośredniego połączenia rur jest silna; Oprzeć się. W przeciwnym razie będziesz prawdopodobnie zobaczyć zakleszczenia lub trzeba zrobić hacky rzeczy jak ta .

2. Uwagi dotyczące Unicode

check_outputzwraca ciąg znaków w Python 2, ale bytesobiekt w Python 3. Warto poświęcić chwilę, aby dowiedzieć się o Unicode, jeśli jeszcze tego nie zrobiłeś.

senderle
źródło
5
Zarówno check_output()i communicate()musisz poczekać, aż proces się zakończy, a poll()otrzymasz wynik w postaci, w jakiej się pojawia. Naprawdę zależy od tego, czego potrzebujesz.
vartec,
2
Nie jestem pewien, czy dotyczy to tylko późniejszych wersji Pythona, ale dla mnie zmienna outbyła typu <class 'bytes'>. Aby uzyskać wynik jako ciąg, musiałem go zdekodować przed wydrukowaniem w następujący sposób:out.decode("utf-8")
PolyMesh
1
@par To nie działa dla ciebie, kiedy zdasz shell=True? Mi to pasuje. Nie potrzebujesz, shlex.splitkiedy zdasz shell=True. shlex.splitjest dla poleceń innych niż powłoki. Myślę, że zamierzam to wyciągnąć, bo to błotniste wody.
nadawca
2
Python 3.5+ dopuszcza argument słowa kluczowego, universal_newlines=Truektóry pozwala przekazać i wydobyć ciągi Unicode w domyślnym kodowaniu systemu. W wersji 3.7 zmieniono jego nazwę na bardziej sensowną text=True.
tripleee
2
W przypadku języka Python 3.6+ można użyć encodingparametru subprocess.runzamiast result.stdout.decode('utf-8'), można użyć subprocess.run(['ls', '-l'], stdout=subprocess.PIPE, encoding='utf-8').
Pierre
191

Jest to o wiele łatwiejsze, ale działa tylko na systemach Unix (w tym Cygwin) i Python2.7.

import commands
print commands.getstatusoutput('wc -l file')

Zwraca krotkę z (return_value, output).

W przypadku rozwiązania, które działa zarówno w Python2, jak i Python3, użyj subprocessmodułu:

from subprocess import Popen, PIPE
output = Popen(["date"],stdout=PIPE)
response = output.communicate()
print response
byte_array
źródło
31
Przestarzałe teraz, ale bardzo przydatne dla starych wersji Pythona bez subprocess.check_output
static_rtti
22
Zauważ, że jest to specyficzne dla Uniksa. Na przykład zawiedzie w systemie Windows.
Zitrax
4
+1 Muszę pracować nad starożytną wersją Pythona 2.4 i było to BARDZO pomocne
javadba
1
Co to jest koleś PIPE, pokaż cały kod: podproces.PIPE
Kyle Bridenstine
@KyleBridenstine możesz edytować odpowiedzi.
Boris
106

Coś w tym stylu:

def runProcess(exe):    
    p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    while(True):
        # returns None while subprocess is running
        retcode = p.poll() 
        line = p.stdout.readline()
        yield line
        if retcode is not None:
            break

Zauważ, że przekierowuję stderr na standardowe wyjście, może to nie być dokładnie to, czego chcesz, ale chcę także komunikatów o błędach.

Ta funkcja zwraca linijkę po linii (zwykle trzeba czekać na zakończenie podprocesu, aby uzyskać wynik jako całość).

W twoim przypadku użycie byłoby:

for line in runProcess('mysqladmin create test -uroot -pmysqladmin12'.split()):
    print line,
vartec
źródło
Pamiętaj, aby wdrożyć jakąś aktywną pętlę, aby uzyskać wyjście, aby uniknąć potencjalnego impasu waiti callfunkcji.
André Caron
@Silver Light: Twój proces prawdopodobnie czeka na dane wejściowe od użytkownika. Spróbuj PIPEpodać wartość stdini zamknąć ten plik, gdy tylko Popenpowróci.
André Caron
4
-1: jest to nieskończona pętla, jeśli retcodejest 0. Czek powinien być if retcode is not None. Nie należy uzyskując puste łańcuchy (nawet pusta jest to co najmniej jeden symbol „\ n”) if line: yield line. Zadzwoń p.stdout.close()na koniec.
jfs
2
Wypróbowałem kod z ls -l / dirname i łamie się on po wyświetleniu dwóch plików, podczas gdy w katalogu jest znacznie więcej plików
Vasilis
3
@fuenfundachtzig: .readlines()nie powróci, dopóki wszystkie dane wyjściowe nie zostaną odczytane, a zatem ulegnie awarii dla dużych danych wyjściowych, które nie mieszczą się w pamięci. Również, aby uniknąć utraty buforowanych danych po wyjściu z podprocesu, powinien istnieć analogif retcode is not None: yield from p.stdout.readlines(); break
jfs
67

Odpowiedź Vartec nie odczytuje wszystkich wierszy, więc stworzyłem wersję, która:

def run_command(command):
    p = subprocess.Popen(command,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.STDOUT)
    return iter(p.stdout.readline, b'')

Użycie jest takie samo, jak zaakceptowana odpowiedź:

command = 'mysqladmin create test -uroot -pmysqladmin12'.split()
for line in run_command(command):
    print(line)
Max Ekman
źródło
6
możesz użyć return iter(p.stdout.readline, b'')zamiast pętli while
jfs
2
To całkiem fajne zastosowanie iteru, nie wiedziałem o tym! Zaktualizowałem kod.
Max Ekman
Jestem pewien, że stdout zachowuje wszystkie dane wyjściowe, jest to obiekt strumieniowy z buforem. Używam bardzo podobnej techniki, aby wyczerpać wszystkie pozostałe dane wyjściowe po zakończeniu Popena, aw moim przypadku za pomocą poll () i readline podczas wykonywania, aby przechwycić również dane wyjściowe na żywo.
Max Ekman
Usunąłem mój wprowadzający w błąd komentarz. Mogę potwierdzić, mogę p.stdout.readline()zwrócić niepuste, wcześniej buforowane dane wyjściowe, nawet jeśli proces potomny już zakończył działanie ( p.poll()nie jest None).
jfs
Ten kod nie działa. Zobacz tutaj stackoverflow.com/questions/24340877/...
thang
61

To trudne, ale bardzo proste rozwiązanie, które działa w wielu sytuacjach:

import os
os.system('sample_cmd > tmp')
print open('tmp', 'r').read()

Plik tymczasowy (tutaj jest tmp) jest tworzony z danymi wyjściowymi polecenia i można z niego odczytać pożądane dane wyjściowe.

Dodatkowa uwaga z komentarzy: Możesz usunąć plik tmp w przypadku zlecenia jednorazowego. Jeśli musisz to zrobić kilka razy, nie musisz usuwać tmp.

os.remove('tmp')
Mehdi Saman Booy
źródło
5
Hacky, ale super prosty + działa wszędzie .. można go połączyć, mktempaby działał w sytuacjach gwintowanych - tak myślę
Prakash Rajagaopal 18.10.16
2
Może najszybsza metoda, ale lepiej dodać os.remove('tmp') aby uczynić ją „bezplikową”.
XuMuK,
@XuMuK Masz rację w przypadku jednorazowej pracy. Jeśli jest to praca powtarzalna, być może usunięcie nie jest konieczne
Mehdi Saman Booy
1
złe dla współbieżności, złe dla funkcji
ponownego wysyłania
1
@ 2mia Oczywiście jest to łatwe bez powodu! Jeśli chcesz użyć tego pliku jako pamięci współdzielonej do jednoczesnych odczytów i zapisów, nie jest to dobry wybór. Ale dla s.th. podobnie jak wyjście polecenia (np. ls lub find lub ...) może to być dobry i szybki wybór. Btw, jeśli potrzebujesz szybkiego rozwiązania prostego problemu, to najlepsze, co myślę. Jeśli potrzebujesz potoku, podproces działa dla ciebie bardziej wydajnie.
Mehdi Saman Booy
44

Miałem ten sam problem, ale wymyśliłem bardzo prosty sposób:

import subprocess
output = subprocess.getoutput("ls -l")
print(output)

Mam nadzieję, że to pomaga

Uwaga: To rozwiązanie jest specyficzne dla Python3, ponieważ subprocess.getoutput()nie działa w Python2

azhar22k
źródło
Jak to rozwiązuje problem PO? Proszę opracować.
RamenChef,
4
Zwraca wynik polecenia jako ciąg znaków, tak proste
azhar22k
1
Oczywiście print jest stwierdzeniem w Pythonie 2. Powinieneś być w stanie zrozumieć, że to odpowiedź na Python 3.
2
@Dev print (s) jest poprawny w Pythonie 2. subprocess.getoutput nie jest.
user48956
2
W większości przypadków jest to, co ludzie prawdopodobnie będą chcieli: łatwe do zapamiętania, nie trzeba dekodować wyników itp. Dziękujemy.
bwv549,
18

Możesz użyć następujących poleceń, aby uruchomić dowolne polecenie powłoki. Użyłem ich na Ubuntu.

import os
os.popen('your command here').read()

Uwaga: Jest to przestarzałe od Pythona 2.6. Teraz musisz użyć subprocess.Popen. Poniżej znajduje się przykład

import subprocess

p = subprocess.Popen("Your command", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
print p.split("\n")
Muhammad Hassan
źródło
2
Przestarzałe od wersji 2.6 - docs.python.org/2/library/os.html#os.popen
Filippo Vitale
1
@FilippoVitale Thanks. Nie wiedziałem, że jest przestarzałe.
Muhammad Hassan
1
Według raspberrypi.stackexchange.com/questions/71547/... os.popen() jest przestarzałe w Pythonie 2.6, ale to nie przestarzałe w Pythonie 3.x, ponieważ w 3.x jest realizowane za pomocą subprocess.Popen().
JL
12

Twój przebieg może się różnić, próbowałem obrócić @ senderle do rozwiązania Vartec w Windows na Pythonie 2.6.5, ale otrzymywałem błędy i żadne inne rozwiązanie nie działało. Mój błąd to:WindowsError: [Error 6] The handle is invalid .

Odkryłem, że muszę przypisać PIPE do każdego uchwytu, aby uzyskać oczekiwany wynik wyjściowy - następujące działało dla mnie.

import subprocess

def run_command(cmd):
    """given shell command, returns communication tuple of stdout and stderr"""
    return subprocess.Popen(cmd, 
                            stdout=subprocess.PIPE, 
                            stderr=subprocess.PIPE, 
                            stdin=subprocess.PIPE).communicate()

i wywołujemy tak: ( [0]pobiera pierwszy element krotki stdout):

run_command('tracert 11.1.0.1')[0]

Po dowiedzeniu się więcej, uważam, że potrzebuję tych argumentów potoku, ponieważ pracuję nad niestandardowym systemem, który używa różnych uchwytów, więc musiałem bezpośrednio kontrolować wszystkie standardowe std.

Aby zatrzymać wyskakujące okienka konsoli (w systemie Windows), wykonaj następujące czynności:

def run_command(cmd):
    """given shell command, returns communication tuple of stdout and stderr"""
    # instantiate a startupinfo obj:
    startupinfo = subprocess.STARTUPINFO()
    # set the use show window flag, might make conditional on being in Windows:
    startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
    # pass as the startupinfo keyword argument:
    return subprocess.Popen(cmd,
                            stdout=subprocess.PIPE, 
                            stderr=subprocess.PIPE, 
                            stdin=subprocess.PIPE, 
                            startupinfo=startupinfo).communicate()

run_command('tracert 11.1.0.1')
Aaron Hall
źródło
1
Ciekawe - to musi być Windows. Dodam notatkę wskazującą na to, na wypadek, gdyby ludzie otrzymywali podobne błędy.
nadawca
używać DEVNULLzamiast z subprocess.PIPE, jeśli nie zapisu / odczytu z potoku w przeciwnym razie może zawiesić proces potomny.
jfs
10

Miałem nieco inny smak tego samego problemu z następującymi wymaganiami:

  1. Przechwytywanie i zwracanie komunikatów STDOUT w miarę ich gromadzenia w buforze STDOUT (tj. W czasie rzeczywistym).
    • @vartec rozwiązał ten Pythonical przy użyciu generatorów i
      powyższego słowa kluczowego „fed”
  2. Wydrukuj wszystkie wiersze STDOUT ( nawet jeśli proces zakończy się, zanim bufor STDOUT będzie można w pełni odczytać )
  3. Nie marnuj cykli procesora na odpytywanie procesu z wysoką częstotliwością
  4. Sprawdź kod powrotu podprocesu
  5. Wydrukuj STDERR (oddzielnie od STDOUT), jeśli otrzymamy niezerowy kod powrotu błędu.

Połączyłem i poprawiłem poprzednie odpowiedzi, aby uzyskać następujące:

import subprocess
from time import sleep

def run_command(command):
    p = subprocess.Popen(command,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE,
                         shell=True)
    # Read stdout from subprocess until the buffer is empty !
    for line in iter(p.stdout.readline, b''):
        if line: # Don't print blank lines
            yield line
    # This ensures the process has completed, AND sets the 'returncode' attr
    while p.poll() is None:                                                                                                                                        
        sleep(.1) #Don't waste CPU-cycles
    # Empty STDERR buffer
    err = p.stderr.read()
    if p.returncode != 0:
       # The run_command() function is responsible for logging STDERR 
       print("Error: " + str(err))

Ten kod zostałby wykonany tak samo jak poprzednie odpowiedzi:

for line in run_command(cmd):
    print(line)
Aelfinn
źródło
1
Czy masz coś przeciwko wyjaśnieniu, w jaki sposób dodanie trybu uśpienia (.1) nie marnuje cykli procesora?
Moataz Elmasry
2
Gdybyśmy nadal dzwonili p.poll()bez przerwy między połączeniami, marnowalibyśmy cykle procesora, wywołując tę ​​funkcję miliony razy. Zamiast tego „dławimy” naszą pętlę, informując system operacyjny, że nie musimy się martwić przez następną 1/10 sekundy, aby mógł wykonywać inne zadania. (Możliwe, że p.poll () również śpi, co powoduje, że nasza instrukcja sleep jest zbędna).
Aelfinn
5

Podział początkowej komendy dla subprocessmoże być trudny i kłopotliwy.

Użyj, shlex.split()aby sobie pomóc.

Przykładowe polecenie

git log -n 5 --since "5 years ago" --until "2 year ago"

Kod

from subprocess import check_output
from shlex import split

res = check_output(split('git log -n 5 --since "5 years ago" --until "2 year ago"'))
print(res)
>>> b'commit 7696ab087a163e084d6870bb4e5e4d4198bdc61a\nAuthor: Artur Barseghyan...'

Bez shlex.split()kodu wyglądałoby to następująco

res = check_output([
    'git', 
    'log', 
    '-n', 
    '5', 
    '--since', 
    '5 years ago', 
    '--until', 
    '2 year ago'
])
print(res)
>>> b'commit 7696ab087a163e084d6870bb4e5e4d4198bdc61a\nAuthor: Artur Barseghyan...'
Artur Barseghyan
źródło
1
shlex.split()jest wygodą, szczególnie jeśli nie wiesz, jak dokładnie działa cytowanie w powłoce; ale ręczne przekonwertowanie tego ciągu na listę ['git', 'log', '-n', '5', '--since', '5 years ago', '--until', '2 year ago']wcale nie jest trudne, jeśli rozumiesz cytowanie.
tripleee
4

Jeśli musisz uruchomić polecenie powłoki na wielu plikach, to załatwiło sprawę.

import os
import subprocess

# Define a function for running commands and capturing stdout line by line
# (Modified from Vartec's solution because it wasn't printing all lines)
def runProcess(exe):    
    p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    return iter(p.stdout.readline, b'')

# Get all filenames in working directory
for filename in os.listdir('./'):
    # This command will be run on each file
    cmd = 'nm ' + filename

    # Run the command and capture the output line by line.
    for line in runProcess(cmd.split()):
        # Eliminate leading and trailing whitespace
        line.strip()
        # Split the output 
        output = line.split()

        # Filter the output and print relevant lines
        if len(output) > 2:
            if ((output[2] == 'set_program_name')):
                print filename
                print line

Edycja: Właśnie zobaczyłem rozwiązanie Maxa Perssona z sugestią JF Sebastiana. Śledziliśmy to i wprowadziliśmy to.

Ethan Strider
źródło
Popenakceptuje albo ciąg znaków, ale wtedy potrzebujesz shell=Truealbo listę argumentów, w którym to przypadku powinieneś podać ['nm', filename]zamiast łańcucha. To drugie jest preferowane, ponieważ powłoka dodaje złożoności, nie podając tutaj żadnej wartości. Przekazywanie ciągu bez shell=Truenajwyraźniej działa w systemie Windows, ale może się to zmienić w każdej kolejnej wersji Pythona.
tripleee
2

Według @senderle, jeśli używasz Python3.6 jak ja:

def sh(cmd, input=""):
    rst = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, input=input.encode("utf-8"))
    assert rst.returncode == 0, rst.stderr.decode("utf-8")
    return rst.stdout.decode("utf-8")
sh("ls -a")

Będzie działał dokładnie tak, jak uruchomisz polecenie w bash

Neo li
źródło
Ponownie tworzysz argumenty słów kluczowych check=True, universal_newlines=True. Innymi słowy, subprocess.run()robi już wszystko, co robi Twój kod.
tripleee
1

Jeśli korzystasz z subprocessmodułu python, możesz osobno obsługiwać STDOUT, STDERR i kod powrotu polecenia. Możesz zobaczyć przykład pełnej implementacji programu wywołującego polecenia. Oczywiście możesz go przedłużyćtry..except jeśli chcesz.

Poniższa funkcja zwraca kod STDOUT, STDERR i Return, aby można było obsługiwać je w innym skrypcie.

import subprocess

def command_caller(command=None)
    sp = subprocess.Popen(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=False)
    out, err = sp.communicate()
    if sp.returncode:
        print(
            "Return code: %(ret_code)s Error message: %(err_msg)s"
            % {"ret_code": sp.returncode, "err_msg": err}
            )
    return sp.returncode, out, err
milanbalazs
źródło
Kolejna słaba reimplementacja subprocess.run(). Nie wymyślaj koła ponownie.
tripleee
0

np. wykonaj ('ls -ahl') rozróżniając trzy / cztery możliwe zwroty i platformy OS:

  1. brak danych wyjściowych, ale przebiegło pomyślnie
  2. wyjmij pustą linię, uruchom pomyślnie
  3. uruchomienie nie powiodło się
  4. wypisz coś, uruchom pomyślnie

funkcja poniżej

def execute(cmd, output=True, DEBUG_MODE=False):
"""Executes a bash command.
(cmd, output=True)
output: whether print shell output to screen, only affects screen display, does not affect returned values
return: ...regardless of output=True/False...
        returns shell output as a list with each elment is a line of string (whitespace stripped both sides) from output
        could be 
        [], ie, len()=0 --> no output;    
        [''] --> output empty line;     
        None --> error occured, see below

        if error ocurs, returns None (ie, is None), print out the error message to screen
"""
if not DEBUG_MODE:
    print "Command: " + cmd

    # https://stackoverflow.com/a/40139101/2292993
    def _execute_cmd(cmd):
        if os.name == 'nt' or platform.system() == 'Windows':
            # set stdin, out, err all to PIPE to get results (other than None) after run the Popen() instance
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
        else:
            # Use bash; the default is sh
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, executable="/bin/bash")

        # the Popen() instance starts running once instantiated (??)
        # additionally, communicate(), or poll() and wait process to terminate
        # communicate() accepts optional input as stdin to the pipe (requires setting stdin=subprocess.PIPE above), return out, err as tuple
        # if communicate(), the results are buffered in memory

        # Read stdout from subprocess until the buffer is empty !
        # if error occurs, the stdout is '', which means the below loop is essentially skipped
        # A prefix of 'b' or 'B' is ignored in Python 2; 
        # it indicates that the literal should become a bytes literal in Python 3 
        # (e.g. when code is automatically converted with 2to3).
        # return iter(p.stdout.readline, b'')
        for line in iter(p.stdout.readline, b''):
            # # Windows has \r\n, Unix has \n, Old mac has \r
            # if line not in ['','\n','\r','\r\n']: # Don't print blank lines
                yield line
        while p.poll() is None:                                                                                                                                        
            sleep(.1) #Don't waste CPU-cycles
        # Empty STDERR buffer
        err = p.stderr.read()
        if p.returncode != 0:
            # responsible for logging STDERR 
            print("Error: " + str(err))
            yield None

    out = []
    for line in _execute_cmd(cmd):
        # error did not occur earlier
        if line is not None:
            # trailing comma to avoid a newline (by print itself) being printed
            if output: print line,
            out.append(line.strip())
        else:
            # error occured earlier
            out = None
    return out
else:
    print "Simulation! The command is " + cmd
    print ""
Jerry T.
źródło
0

Dane wyjściowe można przekierować do pliku tekstowego, a następnie odczytać.

import subprocess
import os
import tempfile

def execute_to_file(command):
    """
    This function execute the command
    and pass its output to a tempfile then read it back
    It is usefull for process that deploy child process
    """
    temp_file = tempfile.NamedTemporaryFile(delete=False)
    temp_file.close()
    path = temp_file.name
    command = command + " > " + path
    proc = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    if proc.stderr:
        # if command failed return
        os.unlink(path)
        return
    with open(path, 'r') as f:
        data = f.read()
    os.unlink(path)
    return data

if __name__ == "__main__":
    path = "Somepath"
    command = 'ecls.exe /files ' + path
    print(execute(command))
PAN
źródło
Pewnie, że tak , ale dlaczego miałbyś chcieć; i dlaczego miałbyś używać pocisku zamiast mijania stdout=temp_file?
tripleee
W zasadzie masz rację, ale w moim przykładzie ecls.exewydaje się, że wdrażasz inne narzędzie wiersza poleceń, więc prosty sposób czasami nie działał.
MR
0

właśnie napisałem mały skrypt bash, aby to zrobić za pomocą curl

https://gist.github.com/harish2704/bfb8abece94893c53ce344548ead8ba5

#!/usr/bin/env bash

# Usage: gdrive_dl.sh <url>

urlBase='https://drive.google.com'
fCookie=tmpcookies

curl="curl -L -b $fCookie -c $fCookie"
confirm(){
    $curl "$1" | grep jfk-button-action | sed -e 's/.*jfk-button-action" href="\(\S*\)".*/\1/' -e 's/\&amp;/\&/g'
}

$curl -O -J "${urlBase}$(confirm $1)"
harish2704
źródło