1464 Stimmen

Ausführen des Shell-Befehls und Erfassen der Ausgabe

Ich möchte eine Funktion schreiben, die ein Shell-Kommando ausführt und seine Ausgabe als String zurückgibt, unabhängig davon, ob es sich um eine Fehler- oder Erfolgsmeldung handelt. Ich möchte nur das gleiche Ergebnis erhalten, das ich mit der Befehlszeile erhalten hätte.

Was wäre ein Codebeispiel, das so etwas tun würde?

Zum Beispiel:

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

print run_command('mysqladmin create test -uroot -pmysqladmin12')
# Sollte etwas wie folgt ausgeben:
# mysqladmin: CREATE DATABASE failed; error: 'Can't create database 'test'; database exists'

30voto

Muhammad Hassan Punkte 13386

Sie können die folgenden Befehle verwenden, um beliebige Shell-Befehle auszuführen. Ich habe sie auf Ubuntu verwendet.

import os
os.popen('Ihr Befehl hier').read()

Hinweis: Dies ist seit Python 2.6 veraltet. Jetzt müssen Sie subprocess.Popen verwenden. Hier ist ein Beispiel:

import subprocess

p = subprocess.Popen("Ihr Befehl", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
print p.split("\n")

19voto

Boris V Punkte 10136

Bei Python 3.7+ verwenden Sie subprocess.run und übergeben Sie capture_output=True:

import subprocess
result = subprocess.run(['echo', 'hello', 'world'], capture_output=True)
print(repr(result.stdout))

Dies gibt Bytes zurück:

b'hello world\n'

Wenn Sie möchten, dass es die Bytes in einen String umwandelt, fügen Sie text=True hinzu:

result = subprocess.run(['echo', 'hello', 'world'], capture_output=True, text=True)
print(repr(result.stdout))

Dies liest die Bytes mit Ihrer Standardcodierung:

'hello world\n'

Wenn Sie manuell eine andere Codierung angeben müssen, verwenden Sie anstelle von text=True encoding="Ihre Codierung":

result = subprocess.run(['echo', 'hello', 'world'], capture_output=True, encoding="utf8")
print(repr(result.stdout))

14voto

The Aelfinn Punkte 11342

Ich hatte eine leicht unterschiedliche Variante des gleichen Problems mit den folgenden Anforderungen:

  1. STDOUT-Nachrichten erfassen und zurückgeben, während sie im STDOUT-Puffer akkumulieren (d.h. in Echtzeit).
    • @vartec hat dies auf Python-Art gelöst, indem er Generatoren und das 'yield'
      Schlüsselwort oben verwendet hat
  2. Alle STDOUT-Zeilen drucken (auch wenn der Prozess beendet wird, bevor der STDOUT-Puffer vollständig gelesen werden kann)
  3. Verschwenden Sie keine CPU-Zyklen durch häufiges Abfragen des Prozesses
  4. Die Rückgabecode des Unterprozesses überprüfen
  5. STDERR drucken (getrennt von STDOUT), wenn wir einen Fehler mit einer von Null verschiedenen Rückgabecode erhalten.

Ich habe vorherige Antworten kombiniert und angepasst, um Folgendes zu erstellen:

import subprocess
from time import sleep

def run_command(command):
    p = subprocess.Popen(command,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE,
                         shell=True)
    # Lese STDOUT aus dem Unterprozess, bis der Puffer leer ist!
    for line in iter(p.stdout.readline, b''):
        if line: # Leere Zeilen nicht drucken
            yield line
    # Dies stellt sicher, dass der Prozess beendet wurde und setzt das Attribut 'returncode'
    while p.poll() is None:                                                                                                                                        
        sleep(.1) # Verschwenden Sie keine CPU-Zyklen
    # Leerer STDERR-Puffer
    err = p.stderr.read()
    if p.returncode != 0:
       # Die run_command() Funktion ist dafür verantwortlich, STDERR zu protokollieren
       print("Fehler: " + str(err))

Dieser Code würde genauso ausgeführt wie frühere Antworten:

for line in run_command(cmd):
    print(line)

12voto

Dein Kilometerstand kann variieren. Ich habe @senderle's Variante von Vartec's Lösung unter Windows mit Python 2.6.5 ausprobiert, aber ich habe Fehlermeldungen erhalten, und keine anderen Lösungen haben funktioniert. Mein Fehler war: WindowsError: [Error 6] Der Handle ist ungültig.

Ich fand heraus, dass ich PIPE jedem Handle zuweisen musste, um die erwartete Ausgabe zu erhalten - folgendes hat für mich funktioniert.

import subprocess

def run_command(cmd):
    """Gibt beim gegebenen Shell-Befehl ein Kommunikationstupel von stdout und stderr zurück"""
    return subprocess.Popen(cmd, 
                            stdout=subprocess.PIPE, 
                            stderr=subprocess.PIPE, 
                            stdin=subprocess.PIPE).communicate()

und rufe es wie folgt auf, ([0] gibt das erste Element des Tupels, stdout):

run_command('tracert 11.1.0.1')[0]

Nachdem ich mehr gelernt habe, glaube ich, dass ich diese Pipe-Argumente benötige, weil ich an einem benutzerdefinierten System arbeite, das verschiedene Handles verwendet, deshalb musste ich alle std's direkt steuern.

Um Konsolen-Popups zu stoppen (mit Windows), mache dies:

def run_command(cmd):
    """Gibt beim gegebenen Shell-Befehl ein Kommunikationstupel von stdout und stderr zurück"""
    # Ein startupinfo-Objekt instanziieren:
    startupinfo = subprocess.STARTUPINFO()
    # Setze das Use-Show-Window-Flag, vielleicht bedingt auf Windows:
    startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
    # Als das startupinfo-Schlüsselwortargument übergeben:
    return subprocess.Popen(cmd,
                            stdout=subprocess.PIPE, 
                            stderr=subprocess.PIPE, 
                            stdin=subprocess.PIPE, 
                            startupinfo=startupinfo).communicate()

run_command('tracert 11.1.0.1')

9voto

Artur Barseghyan Punkte 10480

Das Aufteilen des ersten Befehls für das subprocess kann knifflig und umständlich sein.

Verwenden Sie shlex.split(), um sich selbst zu helfen.

Beispielbefehl

git log -n 5 --since "vor 5 Jahren" --until "vor 2 Jahren"

Der Code

from subprocess import check_output
from shlex import split

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

Ohne shlex.split() würde der Code wie folgt aussehen

res = check_output([
    'git', 
    'log', 
    '-n', 
    '5', 
    '--since', 
    'vor 5 Jahren', 
    '--until', 
    'vor 2 Jahren'
])
print(res)
>>> b'commit 7696ab087a163e084d6870bb4e5e4d4198bdc61a\nAuthor: Artur Barseghyan...'

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X