web-dev-qa-db-de.com

Shell-Befehle in python an subprocess.Popen () übergeben

Ich habe versucht, einen Befehl zu übergeben, der in Shell funktioniert und nur mit Anführungszeichen in der Befehlszeile rund um das Argument "concat:file1|file2" für ffmpeg funktioniert. 

Ich kann diese Arbeit jedoch nicht aus Python mit subprocess.Popen() machen. Hat jemand eine Idee, wie man Zitate an subprozess.Popen übergibt? 

Hier ist der Code:

command = "ffmpeg -i "concat:1.ts|2.ts" -vcodec copy -acodec copy temp.mp4"

output,error = subprocess.Popen(command, universal_newlines=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE).communicate()

Wenn ich dies tue, geht ffmpeg nichts anderes als Zitate rund um das Concat-Segment. Gibt es eine Möglichkeit, diese Zeile erfolgreich an den Befehl subprocess.Popen zu übergeben?

37

Ich würde vorschlagen, die Listenform des Aufrufs anstelle der zitierten String-Version zu verwenden:

command = ["ffmpeg", "-i", "concat:1.ts|2.ts", "-vcodec", "copy",
           "-acodec", "copy", "temp.mp4"]
output,error  = subprocess.Popen(
                    command, universal_newlines=True,
                    stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()

Dies stellt genauer den exakten Satz von Parametern dar, die an den Endprozess übergeben werden, und macht es unnötig, sich mit Shell-Zitaten herumzuschlagen.

Wenn Sie jedoch unbedingt die reine String-Version verwenden möchten, verwenden Sie einfach andere Anführungszeichen (und Shell=True):

command = 'ffmpeg -i "concat:1.ts|2.ts" -vcodec copy -acodec copy temp.mp4'
output,error  = subprocess.Popen(
                    command, universal_newlines=True, Shell=True,
                    stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
37
Amber

Dies funktioniert mit Python 2.7.3 Der Befehl zum Weiterleiten von stderr an stdout wurde seit älteren Versionen von Python geändert:

Fügen Sie dies in eine Datei namens test.py ein:

#!/usr/bin/python
import subprocess

command = 'php -r "echo gethostname();"'
p = subprocess.Popen(command, universal_newlines=True, Shell=True, 
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
text = p.stdout.read()
retcode = p.wait()
print text

Rufe es auf:

python test.py

Es gibt meinen Hostnamen aus, der Apollo ist:

apollo

Lesen Sie das Handbuch zum Unterprozess nach: http://docs.python.org/2/library/subprocess.html

5
Eric Leschinski

Verwenden Sie entweder einfache Anführungszeichen 'around the "whole pattern"', um das Doppel automatisch zu entgehen, oder explizit "escape the \"double quotes\"". Ihr Problem hat nichts mit Popen als solchem ​​zu tun.

Nur für das Protokoll, ich hatte ein Problem, insbesondere mit einem list- basierten Befehl, der an Popen übergeben wurde, der not nicht die richtigen Anführungszeichen um ein Glob-Muster beibehalten würde (dh was in der akzeptierten Antwort ) unter Windows. Das Verknüpfen der Liste in einen String mit ' '.join(cmd) vor der Übergabe an Popen hat das Problem gelöst.

4
j08lue

Ich habe mit einem ähnlichen Problem gearbeitet, mit dem Ausführen eines relativ komplexen Befehls Über ssh. Es gab auch doppelte Anführungszeichen und einfache Anführungszeichen. Weil Ich habe den Befehl über python, ssh, powershell usw. geleitet.

Wenn Sie den Befehl stattdessen einfach in ein Shell-Skript konvertieren und das Skript __.Shell über subprocess.call/Popen/run ausführen können, werden diese Probleme behoben.

Je nachdem, ob Sie Windows oder Linux oder Mac verwenden, fügen Sie das folgende Entweder in ein Shell-Skript ein (script.sh oder script.bat).

ffmpeg -i "concat:1.ts|2.ts" -vcodec copy -acodec copy temp.mp4

Dann kannst du laufen 

import subprocess; subprocess.call(`./script.sh`; Shell=True)

Ohne sich um einfache Anführungszeichen etc kümmern zu müssen

2
alpha_989

Kämpft auch mit einem String-Argument, das Leerzeichen enthält und nicht die Shell = True verwenden soll.

Die Lösung bestand darin, doppelte Anführungszeichen für die inneren Zeichenketten zu verwenden.

args = ['salt', '-G', 'environment:DEV', 'grains.setvals', '{"man_version": "man-dev-2.3"}']

try:
    p = subprocess.Popen(args, stdin=subprocess.PIPE
                             , stdout=subprocess.PIPE
                             , stderr=subprocess.PIPE
                        )
    (stdin,stderr) = p.communicate()
except (subprocess.CalledProcessError, OSError ) as err:
    exit(1)
if p.returncode != 0:
    print("Failure in returncode of command:")
0
Pieter