Shell-Befehl ausführen und Ausgabe ausgeben

908

Ich möchte eine Funktion schreiben, die einen Shell-Befehl ausführt und seine Ausgabe als Zeichenfolge zurückgibt , unabhängig davon, ob es sich um eine Fehler- oder Erfolgsmeldung handelt. Ich möchte nur das gleiche Ergebnis erzielen, das ich mit der Befehlszeile erzielt 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')
# Should output something like:
# mysqladmin: CREATE DATABASE failed; error: 'Can't create database 'test'; database exists'
Silbernes Licht
quelle

Antworten:

1138

Die Antwort auf diese Frage hängt von der von Ihnen verwendeten Python-Version ab. Der einfachste Ansatz ist die Verwendung der subprocess.check_outputFunktion:

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

check_outputführt ein einzelnes Programm aus, das nur Argumente als Eingabe verwendet. 1 Das Ergebnis wird genau so zurückgegeben, wie es gedruckt wurde stdout. Wenn Sie Eingaben schreiben müssen, fahren Sie stdinmit den Abschnitten runoder fort Popen. Wenn Sie komplexe Shell-Befehle ausführen möchten, lesen Sie den Hinweis am shell=TrueEnde dieser Antwort.

Die check_outputFunktion funktioniert auf fast allen Versionen von Python, die noch weit verbreitet sind (2.7+). 2 Für neuere Versionen ist dies jedoch nicht mehr der empfohlene Ansatz.

Moderne Versionen von Python (3.5 oder höher): run

Wenn Sie Python 3.5 oder höher verwenden und keine Abwärtskompatibilität benötigen , wird die neue runFunktion empfohlen. Es bietet eine sehr allgemeine API auf hoher Ebene für das subprocessModul. Übergeben Sie das subprocess.PIPEFlag an das stdoutSchlüsselwortargument, um die Ausgabe eines Programms zu erfassen . Greifen Sie dann auf das stdoutAttribut des zurückgegebenen CompletedProcessObjekts zu:

>>> 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'

Der Rückgabewert ist ein bytesObjekt. Wenn Sie also eine richtige Zeichenfolge wünschen, müssen Sie decodediese verwenden. Angenommen, der aufgerufene Prozess gibt eine UTF-8-codierte Zeichenfolge zurück:

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

Dies kann alles zu einem Einzeiler komprimiert werden:

>>> 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'

Wenn Sie Eingaben an den Prozess übergeben möchten stdin, übergeben Sie ein bytesObjekt an das inputSchlüsselwortargument:

>>> 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'

Sie können Fehler erfassen, indem Sie stderr=subprocess.PIPE(Capture to result.stderr) oder stderr=subprocess.STDOUT(Capture to result.stdoutzusammen mit der regulären Ausgabe) übergeben. Wenn die Sicherheit keine shell=TrueRolle spielt , können Sie auch komplexere Shell-Befehle ausführen, indem Sie wie in den folgenden Hinweisen beschrieben übergeben.

Dies erhöht die Komplexität im Vergleich zur alten Vorgehensweise. Aber ich denke, es lohnt sich: Jetzt können Sie fast alles tun, was Sie brauchen, um die runFunktion alleine zu nutzen.

Ältere Versionen von Python (2.7-3.4): check_output

Wenn Sie eine ältere Version von Python verwenden oder eine bescheidene Abwärtskompatibilität benötigen, können Sie die check_outputFunktion wahrscheinlich wie oben kurz beschrieben verwenden. Es ist seit Python 2.7 verfügbar.

subprocess.check_output(*popenargs, **kwargs)  

Es akzeptiert dieselben Argumente wie Popen(siehe unten) und gibt eine Zeichenfolge zurück, die die Ausgabe des Programms enthält. Der Anfang dieser Antwort enthält ein detaillierteres Verwendungsbeispiel. In Python 3.5 und höher check_outputentspricht dies der Ausführung runmit check=Trueund stdout=PIPEund der Rückgabe nur des stdoutAttributs.

Sie können übergeben, stderr=subprocess.STDOUTum sicherzustellen, dass Fehlermeldungen in der zurückgegebenen Ausgabe enthalten sind. In einigen Versionen von Python kann das Übergeben stderr=subprocess.PIPEan check_outputjedoch Deadlocks verursachen . Wenn die Sicherheit keine shell=TrueRolle spielt , können Sie auch komplexere Shell-Befehle ausführen, indem Sie wie in den folgenden Hinweisen beschrieben übergeben.

Wenn Sie stderrEingaben an den Prozess weiterleiten oder an diesen übergeben müssen, check_outputist dies nicht der Aufgabe gewachsen. Siehe die folgenden PopenBeispiele in diesem Fall.

Komplexe Anwendungen und Legacy-Versionen von Python (2.6 und niedriger): Popen

Wenn Sie eine umfassende Abwärtskompatibilität benötigen oder komplexere Funktionen als check_outputdie bereitgestellten benötigen , müssen Sie direkt mit PopenObjekten arbeiten, die die Low-Level-API für Unterprozesse kapseln.

Der PopenKonstruktor akzeptiert entweder einen einzelnen Befehl ohne Argumente oder eine Liste mit einem Befehl als erstem Element, gefolgt von einer beliebigen Anzahl von Argumenten, die jeweils als separates Element in der Liste aufgeführt sind. shlex.splitkann helfen, Zeichenfolgen in entsprechend formatierte Listen zu analysieren. PopenObjekte akzeptieren auch eine Vielzahl unterschiedlicher Argumente für die Prozess-E / A-Verwaltung und die Konfiguration auf niedriger Ebene.

Das Senden und Erfassen von Ausgaben communicateist fast immer die bevorzugte Methode. Wie in:

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

Oder

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

Wenn Sie festlegen stdin=PIPE, communicatekönnen Sie auch Daten an den Prozess übergeben über 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

Hinweis Aaron Hall Antwort , die das auf einigen Systemen gibt, können Sie festlegen müssen stdout, stderrund stdinalle PIPE(oder DEVNULL) zu erhalten , communicateüberhaupt zu arbeiten.

In einigen seltenen Fällen benötigen Sie möglicherweise eine komplexe Ausgabeerfassung in Echtzeit. Die Antwort von Vartec schlägt einen Weg nach vorne vor, aber andere Methoden als diese communicateneigen zu Deadlocks, wenn sie nicht sorgfältig angewendet werden.

Wie bei allen oben genannten Funktionen können Sie komplexere Shell-Befehle ausführen, wenn die Sicherheit keine Rolle spielt shell=True.

Anmerkungen

1. Shell-Befehle ausführen: das shell=TrueArgument

Normalerweise wird jeder Anruf run, check_outputoder der PopenKonstruktor führt ein einziges Programm . Das bedeutet, dass es keine ausgefallenen Pfeifen im Bash-Stil gibt. Wenn Sie komplexe Shell-Befehle ausführen möchten, können Sie übergeben shell=True, was alle drei Funktionen unterstützen.

Dies wirft jedoch Sicherheitsbedenken auf . Wenn Sie mehr als nur Light Scripting ausführen, ist es möglicherweise besser, jeden Prozess einzeln aufzurufen und die Ausgabe von jedem als Eingabe an die nächste über zu übergeben

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

Oder

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

Die Versuchung, Rohre direkt anzuschließen, ist groß; widerstehen. Andernfalls werden Sie wahrscheinlich Deadlocks sehen oder Hacky Dinge wie zu tun dies .

2. Unicode-Überlegungen

check_outputGibt eine Zeichenfolge in Python 2 zurück, aber ein bytesObjekt in Python 3. Es lohnt sich, sich einen Moment Zeit zu nehmen , um etwas über Unicode zu lernen, wenn Sie dies noch nicht getan haben.

senderle
quelle
5
Sowohl mit check_output()als auch communicate()Sie müssen warten, bis der Vorgang abgeschlossen ist, und poll()Sie erhalten die Ausgabe so wie sie kommt. Kommt wirklich darauf an was du brauchst.
Vartec
2
Ich bin mir nicht sicher, ob dies nur für spätere Versionen von Python gilt, aber die Variable outwar <class 'bytes'>für mich vom Typ . Um die Ausgabe als Zeichenfolge zu erhalten, musste ich sie vor dem Drucken wie out.decode("utf-8")
folgt
1
@par Das funktioniert bei dir nicht, wenn du bestanden hast shell=True? Für mich geht das. Sie brauchen nicht, shlex.splitwenn Sie passieren shell=True. shlex.splitist für Nicht-Shell-Befehle. Ich denke, ich werde das bisschen rausnehmen, weil das das Wasser trübt.
senderle
2
Python 3.5+ ermöglicht ein Schlüsselwortargument, mit universal_newlines=Truedem Sie Unicode-Zeichenfolgen in der Standardcodierung des Systems übergeben und entfernen können . In 3.7 wurde dies in vernünftiger umbenannt text=True.
Tripleee
2
Für Python 3.6+ können Sie den encodingParameter verwenden, subprocess.runanstatt result.stdout.decode('utf-8')ihn zu verwenden subprocess.run(['ls', '-l'], stdout=subprocess.PIPE, encoding='utf-8').
Pierre
191

Dies ist viel einfacher, funktioniert aber nur unter Unix (einschließlich Cygwin) und Python2.7.

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

Es gibt ein Tupel mit (return_value, output) zurück.

Verwenden Sie für eine Lösung, die sowohl in Python2 als auch in Python3 funktioniert, subprocessstattdessen das Modul:

from subprocess import Popen, PIPE
output = Popen(["date"],stdout=PIPE)
response = output.communicate()
print response
byte_array
quelle
31
Jetzt veraltet, aber sehr nützlich für alte Python-Versionen ohne
subprocess.check_output
22
Beachten Sie, dass dies Unix-spezifisch ist. Es wird zum Beispiel unter Windows fehlschlagen.
Zitrax
4
+1 Ich muss an der alten Version von Python 2.4 arbeiten und das war SEHR hilfreich
javadba
1
Was ist PIPE-Typ? Komm und zeige den vollständigen Code: subprocess.PIPE
Kyle Bridenstine
@KyleBridenstine können Sie Antworten bearbeiten.
Boris
106

So ähnlich:

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

Beachten Sie, dass ich stderr nach stdout umleite. Möglicherweise ist es nicht genau das, was Sie möchten, aber ich möchte auch Fehlermeldungen.

Diese Funktion liefert zeilenweise (normalerweise müssen Sie warten, bis der Unterprozess abgeschlossen ist, um die Ausgabe als Ganzes zu erhalten).

Für Ihren Fall wäre die Verwendung:

for line in runProcess('mysqladmin create test -uroot -pmysqladmin12'.split()):
    print line,
vartec
quelle
Stellen Sie sicher, dass Sie eine Art aktive Schleife implementieren, um die Ausgabe zu erhalten und mögliche Deadlocks waitund callFunktionen zu vermeiden .
André Caron
@Silver Light: Ihr Prozess wartet wahrscheinlich auf Eingaben des Benutzers. Versuchen Sie, einen PIPEWert für stdindiese Datei anzugeben und sie zu schließen, sobald sie Popenzurückgegeben wird.
André Caron
4
-1: Es ist eine Endlosschleife, wenn das retcodeist 0. Die Prüfung sollte sein if retcode is not None. Sie sollten keine leeren Zeichenfolgen ausgeben (selbst eine leere Zeile besteht aus mindestens einem Symbol '\ n') : if line: yield line. Rufen Sie p.stdout.close()am Ende an.
JFS
2
Ich habe versucht, den Code mit ls -l / dirname und es bricht nach dem Auflisten von zwei Dateien, während es viel mehr Dateien im Verzeichnis gibt
Vasilis
3
@fuenfundachtzig: .readlines()kehrt erst zurück, wenn alle Ausgaben gelesen wurden, und bricht daher bei großen Ausgaben ab, die nicht in den Speicher passen. Um zu vermeiden, dass gepufferte Daten nach dem Beenden des if retcode is not None: yield from p.stdout.readlines(); break
Unterprozesses
67

Die Antwort von Vartec liest nicht alle Zeilen, deshalb habe ich eine Version erstellt, die Folgendes tat:

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

Die Verwendung entspricht der akzeptierten Antwort:

command = 'mysqladmin create test -uroot -pmysqladmin12'.split()
for line in run_command(command):
    print(line)
Max Ekman
quelle
6
Sie könnten return iter(p.stdout.readline, b'')anstelle der while-Schleife verwenden
jfs
2
Das ist eine ziemlich coole Verwendung von iter, wusste das nicht! Ich habe den Code aktualisiert.
Max Ekman
Ich bin mir ziemlich sicher, dass stdout die gesamte Ausgabe behält, es ist ein Stream-Objekt mit einem Puffer. Ich verwende eine sehr ähnliche Technik, um alle verbleibenden Ausgaben nach Abschluss eines Popens zu erschöpfen, und in meinem Fall verwende ich poll () und readline während der Ausführung, um die Ausgabe auch live zu erfassen.
Max Ekman
Ich habe meinen irreführenden Kommentar entfernt. Ich kann bestätigen, p.stdout.readline()kann die nicht leere zuvor gepufferte Ausgabe zurückgeben, auch wenn der untergeordnete Prozess bereits beendet wurde ( p.poll()nicht None).
JFS
Dieser Code funktioniert nicht. Siehe hier stackoverflow.com/questions/24340877/…
thang
61

Dies ist eine knifflige, aber supereinfache Lösung, die in vielen Situationen funktioniert:

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

Mit der Ausgabe des Befehls wird eine temporäre Datei (hier ist tmp) erstellt, aus der Sie die gewünschte Ausgabe lesen können.

Zusätzlicher Hinweis aus den Kommentaren: Sie können die tmp-Datei bei einmaligen Aufträgen entfernen. Wenn Sie dies mehrmals tun müssen, müssen Sie den tmp nicht löschen.

os.remove('tmp')
Mehdi Saman Booy
quelle
5
Hacky aber super einfach + funktioniert überall .. kann es mit kombinieren mktemp, damit es in Thread-Situationen funktioniert, denke ich
Prakash Rajagaopal
2
Vielleicht die schnellste Methode, aber besser hinzufügen os.remove('tmp'), um sie "fileless" zu machen.
XuMuK
@XuMuK Sie haben Recht bei einem einmaligen Job. Wenn es sich um eine sich wiederholende Arbeit handelt, ist möglicherweise kein Löschen erforderlich
Mehdi Saman Booy
1
schlecht für die Parallelität, schlecht für wiedereintrittsfähige Funktionen, schlecht dafür, das System nicht so zu verlassen, wie es vor dem Start war (keine Bereinigung)
2mia
1
@ 2mia Offensichtlich ist es aus einem Grund einfach! Wenn Sie die Datei als eine Art gemeinsam genutzten Speicher für gleichzeitige Lese- und Schreibvorgänge verwenden möchten, ist dies keine gute Wahl. Aber für s.th. Wie die Ausgabe eines Befehls (z. B. ls oder find oder ...) kann dies eine gute und schnelle Wahl sein. Übrigens, wenn Sie eine schnelle Lösung für ein einfaches Problem brauchen, ist es das Beste, was ich denke. Wenn Sie eine Pipeline benötigen, arbeitet der Unterprozess für Sie effizienter.
Mehdi Saman Booy
44

Ich hatte das gleiche Problem, fand aber einen sehr einfachen Weg, dies zu tun:

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

Hoffe es hilft aus

Hinweis: Diese Lösung ist Python3-spezifisch, da subprocess.getoutput()sie in Python2 nicht funktioniert

azhar22k
quelle
Wie löst dies das Problem des OP? Bitte erläutern Sie.
RamenChef
4
Es gibt die Ausgabe des Befehls als Zeichenfolge zurück, so einfach wie das
azhar22k
1
Natürlich ist print eine Aussage zu Python 2. Sie sollten in der Lage sein herauszufinden, dass dies eine Python 3-Antwort ist.
2
@ Dev print (s) ist gültig für Python 2. subprocess.getoutput ist nicht gültig.
user48956
2
Für die meisten Anwendungsfälle ist dies wahrscheinlich das, was die Leute wahrscheinlich wollen: leicht zu merken, die Ergebnisse nicht entschlüsseln müssen usw. Vielen Dank.
bwv549
18

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

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

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

import subprocess

p = subprocess.Popen("Your command", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
print p.split("\n")
Muhammad Hassan
quelle
2
Veraltet seit Version 2.6 - docs.python.org/2/library/os.html#os.popen
Filippo Vitale
1
@FilippoVitale Danke. Ich wusste nicht, dass es veraltet ist.
Muhammad Hassan
1
Laut raspberrypi.stackexchange.com/questions/71547/… os.popen() ist es in Python 2.6 veraltet, in Python 3.x jedoch nicht veraltet, da es in 3.x mit implementiert wird subprocess.Popen().
JL
12

Ihr Kilometerstand kann variieren. Ich habe versucht, @ senderles Version von Vartecs Lösung in Windows unter Python 2.6.5 zu testen, aber es wurden Fehler angezeigt, und es funktionierten keine anderen Lösungen. Mein Fehler war : WindowsError: [Error 6] The handle is invalid.

Ich stellte fest, dass ich jedem Handle PIPE zuweisen musste, damit es die erwartete Ausgabe zurückgab - das Folgende funktionierte für mich.

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()

und rufe so auf ( [0]bekommt 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 brauche, weil ich an einem benutzerdefinierten System arbeite, das verschiedene Handles verwendet, also musste ich alle Standards direkt steuern.

Gehen Sie folgendermaßen vor, um Konsolen-Popups (unter Windows) zu stoppen:

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
quelle
1
Interessant - das muss eine Windows-Sache sein. Ich werde einen Hinweis hinzufügen, der darauf hinweist, falls Leute ähnliche Fehler bekommen.
senderle
verwendet DEVNULLanstelle von , subprocess.PIPEwenn Sie nicht schreiben / lesen aus einem Rohr sonst können Sie das Kind Prozess hängen.
JFS
10

Ich hatte einen etwas anderen Geschmack des gleichen Problems mit den folgenden Anforderungen:

  1. Erfassen und geben Sie STDOUT-Nachrichten zurück, wenn sie sich im STDOUT-Puffer ansammeln (dh in Echtzeit).
    • @vartec löste dieses Problem Pythonisch mit seiner Verwendung von Generatoren und dem
      obigen Schlüsselwort'ield '
  2. Drucken Sie alle STDOUT-Zeilen ( auch wenn der Prozess beendet wird, bevor der STDOUT-Puffer vollständig gelesen werden kann ).
  3. Verschwenden Sie keine CPU-Zyklen, die den Prozess mit hoher Frequenz abfragen
  4. Überprüfen Sie den Rückkehrcode des Unterprozesses
  5. Drucken Sie STDERR (getrennt von STDOUT), wenn ein Fehlerrückgabecode ungleich Null angezeigt wird.

Ich habe frühere Antworten kombiniert und optimiert, um Folgendes zu erhalten:

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))

Dieser Code würde genauso ausgeführt wie die vorherigen Antworten:

for line in run_command(cmd):
    print(line)
Der Aelfinn
quelle
1
Haben Sie etwas dagegen zu erklären, wie das Hinzufügen von Schlaf (.1) keine CPU-Zyklen verschwendet?
Moataz Elmasry
2
Wenn wir p.poll()zwischen den Aufrufen ohne Schlaf weiter telefonieren würden, würden wir CPU-Zyklen verschwenden, indem wir diese Funktion millionenfach aufrufen. Stattdessen "drosseln" wir unsere Schleife, indem wir dem Betriebssystem mitteilen, dass wir uns nicht um die nächste 1/10 Sekunde kümmern müssen, damit es andere Aufgaben ausführen kann. (Es ist möglich, dass p.poll () auch schläft, wodurch unsere Schlafanweisung überflüssig wird.)
Die Aelfinn
5

Das Aufteilen des Anfangsbefehls für das subprocesskann schwierig und umständlich sein.

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

Beispielbefehl

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

Der Code

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...'

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

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
quelle
1
shlex.split()Dies ist eine Annehmlichkeit, insbesondere wenn Sie nicht wissen, wie genau das Zitieren in der Shell funktioniert. Das manuelle Konvertieren dieser Zeichenfolge in die Liste ['git', 'log', '-n', '5', '--since', '5 years ago', '--until', '2 year ago']ist jedoch überhaupt nicht schwierig, wenn Sie das Zitieren verstehen.
Tripleee
4

Wenn Sie einen Shell-Befehl für mehrere Dateien ausführen müssen, hat dies den Trick für mich getan.

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

Bearbeiten: Ich habe gerade die Lösung von Max Persson mit dem Vorschlag von JF Sebastian gesehen. Ging voran und nahm das auf.

Ethan Strider
quelle
PopenAkzeptiert entweder eine Zeichenfolge, aber dann benötigen Sie shell=Trueoder eine Liste von Argumenten. In diesem Fall sollten Sie ['nm', filename]anstelle einer Zeichenfolge übergeben. Letzteres ist vorzuziehen, da die Shell die Komplexität erhöht, ohne hier einen Wert bereitzustellen. Das Übergeben einer Zeichenfolge ohne shell=Truefunktioniert anscheinend unter Windows, dies kann sich jedoch in jeder nächsten Python-Version ändern.
Tripleee
2

Laut @senderle, wenn Sie Python3.6 wie ich verwenden:

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")

Verhält sich genau so, wie Sie den Befehl in bash ausführen

Neo li
quelle
Sie erfinden die Schlüsselwortargumente neu check=True, universal_newlines=True. Mit anderen Worten, macht subprocess.run()schon alles, was Ihr Code macht.
Tripleee
1

Wenn Sie das subprocessPython-Modul verwenden, können Sie den Befehlscode STDOUT, STDERR und return separat behandeln. Sie können ein Beispiel für die vollständige Implementierung des Befehlsaufrufers sehen. Natürlich können Sie es erweitern, try..exceptwenn Sie möchten.

Die folgende Funktion gibt den STDOUT-, STDERR- und Return-Code zurück, damit Sie sie im anderen Skript verarbeiten können.

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
quelle
Eine weitere schlechte Neuimplementierung von subprocess.run(). Das Rad nicht neu erfinden.
Tripleee
0

Beispiel: Führen Sie ('ls -ahl') differenzierte drei / vier mögliche Rückgaben und Betriebssystemplattformen aus:

  1. Keine Ausgabe, aber erfolgreich ausgeführt
  2. leere Zeile ausgeben, erfolgreich ausführen
  3. Lauf fehlgeschlagen
  4. etwas ausgeben, erfolgreich ausführen

Funktion unten

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.
quelle
0

Die Ausgabe kann in eine Textdatei umgeleitet und dann zurückgelesen werden.

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))
HERR
quelle
Sicher kann es, aber warum sollten Sie wollen; und warum sollten Sie die Shell verwenden, anstatt zu übergeben stdout=temp_file?
Tripleee
Eigentlich haben Sie im Allgemeinen Recht, aber in meinem Beispiel ecls.exescheint das ein anderes Befehlszeilentool bereitzustellen, sodass der einfache Weg manchmal nicht funktioniert hat.
MR
0

habe gerade ein kleines Bash-Skript geschrieben, um dies mit Curl zu tun

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
quelle