Ich versuche, eine ausführbare Datei namens foo aufzurufen und ihr einige Befehlszeilenargumente zu übergeben. Ein externes Skript ruft die ausführbare Datei auf und verwendet den folgenden Befehl:

./main/foo --config config_file 2>&1 | /usr/bin/tee temp.log

Das Skript verwendet Popen, um diesen Befehl wie folgt auszuführen:

from subprocess import Popen
from subprocess import PIPE
def run_command(command, returnObject=False):
    cmd = command.split(' ')
    print('%s' % cmd)
    p = None
    print('command : %s' % command)
    if returnObject:
        p = Popen(cmd)
    else:
        p = Popen(cmd)
        p.communicate()
        print('returncode: %s' % p.returncode)
        return p.returncode
    return p

command = "./main/foo --config config_file 2>&1 | /usr/bin/tee temp.log
"
run_command(command)

Dadurch werden jedoch zusätzliche Argumente ['2> & 1', '|', '/ usr / bin / tee', 'temp.log'] an die ausführbare Datei foo übergeben.

Wie kann ich diese zusätzlichen Argumente beseitigen, die an foo übergeben werden, während die Funktionalität beibehalten wird? Ich habe Shell = True ausprobiert, aber ich habe gelesen, wie ich es aus Sicherheitsgründen vermeiden kann (Shell-Injection-Angriff). Auf der Suche nach einer ordentlichen Lösung.

Vielen Dank

UPDATE: - Die Datei wurde nach dem Befehl tee aktualisiert

0
shank22 12 Aug. 2015 im 00:10

3 Antworten

Beste Antwort

Die Saite

./main/foo --config config_file 2>&1 | /usr/bin/tee >temp.log

... ist voller Shell-Konstrukte. Diese haben für nichts ohne eine Shell im Spiel eine Bedeutung. Sie haben also zwei Möglichkeiten:

  • Setze shell=True
  • Ersetzen Sie sie durch nativen Python-Code.

Zum Beispiel ist 2>&1 dasselbe wie das Übergeben von stderr=subprocess.STDOUT an Popen, und Ihr Tee kann - da seine Ausgabe umgeleitet wird und keine Argumente übergeben werden - einfach durch {{ersetzt werden X3}}.


Somit:

p = subprocess.Popen(['./main/foo', '--config', 'config_file'],
  stderr=subprocess.STDOUT,
  stdout=open('temp.log', 'w'))

... oder wenn Sie den Befehl tee wirklich wollten wollten, ihn aber nur falsch verwendeten (dh wenn Sie tee temp.log wollten, nicht tee >temp.log ):

p1 = subprocess.Popen(['./main/foo', '--config', 'config_file'],
  stderr=subprocess.STDOUT,
  stdout=subprocess.PIPE)
p2 = subprocess.Popen(['tee', 'temp.log'], stdin=p1.stdout)
p1.stdout.close() # drop our own handle so p2's stdin is the only handle on p1.stdout
stdout, _ = p2.communicate()

Wenn Sie dies in eine Funktion einwickeln und den Erfolg für beide Enden überprüfen, könnte dies folgendermaßen aussehen:

def run():
    p1 = subprocess.Popen(['./main/foo', '--config', 'config_file'],
      stderr=subprocess.STDOUT,
      stdout=subprocess.PIPE)
    p2 = subprocess.Popen(['tee', 'temp.log'], stdin=p1.stdout)
    p1.stdout.close() # drop our own handle so p2's stdin is the only handle on p1.stdout
    # True if both processes were successful, False otherwise
    return (p2.wait() == 0 && p1.wait() == 0)

Übrigens - wenn Sie shell=True verwenden und den Exit-Status von foo anstelle von tee zurückgeben möchten, werden die Dinge etwas interessanter. Folgendes berücksichtigen:

p = subprocess.Popen(['bash', '-c', 'set -o pipefail; ' + command_str])

... die Bash-Erweiterung pipefail zwingt die Shell zum Beenden mit dem Status der ersten fehlgeschlagenen Pipeline-Komponente (und 0, wenn keine Komponenten ausfallen), anstatt nur den Exit-Status der letzten Komponente zu verwenden.

3
Charles Duffy 27 Aug. 2015 im 19:13

Hier sind ein paar "nette" Codebeispiele zusätzlich zu der Erklärung aus der Antwort von @Charles Duffy.

So führen Sie den Shell-Befehl in Python aus:

#!/usr/bin/env python
from subprocess import check_call

check_call("./main/foo --config config_file 2>&1 | /usr/bin/tee temp.log",
           shell=True)

Ohne die Schale:

#!/usr/bin/env python
from subprocess import Popen, PIPE, STDOUT

tee = Popen(["/usr/bin/tee", "temp.log"], stdin=PIPE)
foo = Popen("./main/foo --config config_file".split(), 
            stdout=tee.stdin, stderr=STDOUT)
pipestatus = [foo.wait(), tee.wait()]

Hinweis: Verwenden Sie "command arg".split() nicht mit nicht wörtlichen Zeichenfolgen.

Siehe Wie verwende ich subprocess.Popen, um mehrere Prozesse über Pipes zu verbinden?

1
Community 23 Mai 2017 im 12:19

Sie können Antworten auf zwei StackOverflow-Fragen kombinieren:
1. mehrere Unterprozesse zusammenführen
x | y Problem
2. Zusammenführen von stdout und stderr des Unterprozesses eines Python-Skripts (wobei sie unterscheidbar bleiben)
2>&1 Problem

0
AnFi 11 Aug. 2015 im 22:31