subprocess.popen -> Ausgabe anzeigen und weiterverarbeiten

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
mcdaniels
User
Beiträge: 168
Registriert: Mittwoch 18. August 2010, 19:53

Donnerstag 22. März 2012, 09:49

Hallo zusammen!
Ich kämpfe im Moment grade mit "subprocess.popen". Hintergrund ist, dass ich aus Python heraus eine Batch-Datei (Windows) aufrufen will, dieser Batchdatei dann per Python 2 Parameter mitgebe und gerne die "Ausgabe" dieser Aktion geliefert bekommen würde.

Nur um zu testen, ob mein "Programm" überhaupt läuft habe ich mal das hier versucht:

Code: Alles auswählen

import subprocess
befehl = 'cmd'
ausgabe = subprocess.Popen(befehl)
exit
Gut, es wird eine Dos-Box geöffnet und bleibt offen (soweit klar).

Wenn ich das Ganze aber nun mit einer Batch Datei mache, zuckt nur kurz das "Dosfenster" auf (ich nehme an, dass die Batchdatei einfach ruck zuck abgearbeitet wird) und das war es. Es erfolgt also keine Ausgabe auf den Bildschirm.

Nun dachte ich, ich könnte das Ganze mittels "STDOUT" ausgeben. Dem ist aber nicht so.

Meine Fragen dazu:
1.) Wie kann ich anzeigen (ausgeben) lassen, was die Batch macht?
2.) Wie könnte man die Ausgabe direkt in Python weiter verarbeiten?

LG und vielen Dank!

Daniel
Zuletzt geändert von mcdaniels am Montag 26. März 2012, 10:45, insgesamt 1-mal geändert.
BlackJack

Donnerstag 22. März 2012, 09:55

@mcdaniels: Das was Du `ausgabe` nennst ist ein Objekt was den Prozess darstellt und über das man mit dem Prozess kommunizieren kann. Die Dokumentation enthält einige Beispiele zur Umleitung von Ein- und Ausgaben.
mcdaniels
User
Beiträge: 168
Registriert: Mittwoch 18. August 2010, 19:53

Donnerstag 22. März 2012, 09:59

@Blackjack: Danke, da wären wir wieder bei meiner damaligen "Objektsache" :-) Ich schau mir die Doku nochmal an. Ausserdem bin ich auch hier noch am lesen: http://docs.python.org/py3k/library/
Nicht ganz so einfach, wenn man leider (wiedermal) lange Zeit nichts mit Python gemacht hat.

Edit: Die Methode .check_output scheint mir hier ein geeigneter Kandidat zu sein, um den Output "abzugreifen". Edit2: (doch nicht wirklich)
Benutzeravatar
/me
User
Beiträge: 3276
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Donnerstag 22. März 2012, 11:27

Hier ist ein Beispiel.

Code: Alles auswählen

from subprocess import Popen

comm = subprocess.Popen(['ipconfig'], stdout=subprocess.PIPE)
data = comm.communicate()
print data
Dokumentation zu subprocess.Popen: http://docs.python.org/library/subproce ... en-objects. Unter Python 3 hat sich da IMHO nichts geändert.
mcdaniels
User
Beiträge: 168
Registriert: Mittwoch 18. August 2010, 19:53

Donnerstag 22. März 2012, 11:42

Servus!
Danke dir, ein Beispiel ist immer gut.

Läuft bei mir nur in der Form (wobei print() klar ist)

Code: Alles auswählen

import subprocess
comm = subprocess.Popen(['ipconfig'], stdout=subprocess.PIPE)
data = comm.communicate()
print (data)
Zuletzt geändert von mcdaniels am Montag 26. März 2012, 10:46, insgesamt 1-mal geändert.
Benutzeravatar
/me
User
Beiträge: 3276
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Donnerstag 22. März 2012, 12:19

Das war ein Fehler in meinem Beispiel. Beim Copy, Paste & Delete habe ich die verkehrte Zeile gelöscht.

Du kannst je nach Geschmack eine der beiden folgenden Varianten verwenden:

Code: Alles auswählen

import subprocess
comm = subprocess.Popen(['ipconfig'], stdout=subprocess.PIPE)

Code: Alles auswählen

from subprocess import Popen
comm = Popen(['ipconfig'], stdout=subprocess.PIPE)
Ich bevorzuge meistens die erste Variante.
mcdaniels
User
Beiträge: 168
Registriert: Mittwoch 18. August 2010, 19:53

Donnerstag 22. März 2012, 13:43

Danke!
Mein "Projekt" nimmt ungeahnte Dimensionen an :D

Ziel ist es, dass *.pdf Dateien aus einem Verzeichnis eingelesen werden (Liste). Danach soll mittels einem PDF Signer (Portable Signer) die PDF Datei signiert werden. Mein PDF-einlesen Code sieht so aus:

Code: Alles auswählen

import glob
pdf_dateien = glob.glob('*.pdf')
anzahl_dateien = len(pdf_dateien)

for zaehler in range (-1,int(anzahl_dateien-1)):
                     print(pdf_dateien[zaehler])
                     signieren = pdf_dateien[zaehler] + ' -o ' + pdf_dateien[zaehler] + '_sig.pdf' + ' -s acert-pkcs12.p12 -p passwort'
                     print (signieren)

Statt der Printfunktion muss ich dann hier auf subprocess.Popen zurückgreifen, das eine Bat aufruft und dem Portable Signer die Parameter übergibt. Bin mal gespannt, ob ich das zusammen bringe. Vor allem frage ich mich grade, ob mir das Programm beim Durchgehen der gefundenen PDF Dateien immer schön brav wartet bis der jeweilige Signierungsvorgang pro Datei fertig ist, oder ob x-fache Signierungsvorgänge gestartet werden. (sonst eventuell ein Fall für popen.wait...)

Dass ich beim Vergeben des 2ten Namens (mit der Endung _sig.pdf) ein Problem habe, weiß ich.

EDIT: Wie befürchtet klappt die Parameterübergabe nicht. Hatte mir das auch zu einfach vorgestellt. Dachte es klappt so:

Code: Alles auswählen

comm = subprocess.Popen(['signpython.bat' + signieren], stdout=subprocess.PIPE)
:)

Muss wohl auch mit stdin arbeiten?

Naja wenn ich das Ganze mit

Code: Alles auswählen

os.system(signieren)
mache und in die Variable signieren den gesamten Batchcode inkl. die Variablen reinpacke läuft es. Ist aber eigentlich nicht das was ich wollte, da man ja eher nicht auf os.system zurückgreifen soll und hier eigentlich subprocess angesagt wäre.

LG
Daniel
Benutzeravatar
/me
User
Beiträge: 3276
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Donnerstag 22. März 2012, 15:24

mcdaniels hat geschrieben:

Code: Alles auswählen

import glob
pdf_dateien = glob.glob('*.pdf')
anzahl_dateien = len(pdf_dateien)

for zaehler in range (-1,int(anzahl_dateien-1)):
                     print(pdf_dateien[zaehler])
                     signieren = pdf_dateien[zaehler] + ' -o ' + pdf_dateien[zaehler] + '_sig.pdf' + ' -s acert-pkcs12.p12 -p passwort'
                     print (signieren)
Das ist kein idiomatisches Python. Schleifen schreibt man in Python praktischerweise so, dass direkt über die Liste iteriert wird.

Code: Alles auswählen

data = ['file1.pdf', 'file2.pdf', 'filen.pdf']

# unübersichtlich
for i in range(len(data)):
    print(data[i])

# übersichtlicher und schneller als die andere Version
for filename in data:
    print(filename)
Die Übergabe von Parametern an Popen erfolgt dann als Liste, nicht als ein langer String.
mcdaniels
User
Beiträge: 168
Registriert: Mittwoch 18. August 2010, 19:53

Donnerstag 22. März 2012, 16:01

Hallo /me - Danke für deine Geduld.

Momentan sieht das Ganze so aus:

Code: Alles auswählen

import glob
import os

def start_sig():
    pdf_dateien = glob.glob('*.pdf')
    for filename in pdf_dateien:
        print('Signiere: ' + str(filename))
        signieren = 'java -jar PortableSigner.jar -b de -c "Dieses Dokument wurde amtssigniert. Informationen zur Pruefung finden Sie unter http://www.meineseite.at " -i Bildmarke.jpg -n -t ' + '"' + str(filename) + '"' + ' -o ''' + '"' + str(filename).rstrip('.pdf') + '_sig.pdf"' + ' -s acert.p12 -p Passwort'
        os.system(signieren)


def verschieben_sig():
    try:
        kopieren = 'copy *_sig.pdf sig-pdf'
        loeschen = 'del *_sig.pdf'
        os.system(kopieren)
        os.system(loeschen)
    except:
        print('Es ist ein Fehler aufgetreten')
Wahrscheinlich schlagt man als Profi die Hände über dem Kopf zusammen. Greift bei meiner Funktion verschieben_sig() der try except block eigentlich, wenn ein Fehler auftritt. Ich habe nicht den Eindruck.

Das mit dem Anhängen als Liste bei popen habe ich noch nicht wirklich verstanden. Wird dann hier ein Listeintrag pro Parameter verwendet:

Bsp:

Code: Alles auswählen

['-i Bildmarke.jpg', '-n','-t','str(filename)']


LG
Daniel
mcdaniels
User
Beiträge: 168
Registriert: Mittwoch 18. August 2010, 19:53

Freitag 23. März 2012, 10:21

Hallo nochmals!
Habe es nun so versucht:

Code: Alles auswählen

comm = subprocess.Popen(['signpython.bat', str(filename),' -o ', str(filename).rstrip('.pdf') + '-sig.pdf','-s acert.p12 -p Passwort'])
Es wird aber nur die .bat ausgeführt. Die Parameter werden nicht angehängt.

Wäre dankbar, falls ihr Tips für mich hättet.

LG
Daniel
BlackJack

Freitag 23. März 2012, 12:29

@mcdaniels: Die Parameter werden sehr wohl angehängt. Die Liste muss den Inhalt haben, den das aufgerufene Programm als Argumentliste erwartet. Also das was in Python in `sys.argv` ankommen würde. Das aufgerufene Programm wird weder mit ' -o ' noch mit '-s acert.p12 -p Passwort' etwas anfangen können. Das sind beides keine Elemente die es erwartet. Es sucht nach Elementen wie '-o', '-s' und '-p' und erwartet danach jeweils ein Element mit dem Wert für die Option.

`rstrip()` tut nicht das was Du denkst was es tut:

Code: Alles auswählen

In [95]: 'proof.pdf'.rstrip('.pdf')
Out[95]: 'proo'

In [96]: 'sepp.pdf'.rstrip('.pdf')
Out[96]: 'se'
mcdaniels
User
Beiträge: 168
Registriert: Mittwoch 18. August 2010, 19:53

Freitag 23. März 2012, 14:41

Hallo Blackjack!

Stimmt, das hatte ich nicht bedacht, was rstrip angeht. Danke für den Hinweis. Könntest du eventuell das mit den Argumenten noch präzisieren?

Meinst du ich muss java -jar PortableSigner.jar direkt aus Python heraus aufrufen, weil die .BAT diese Parameter nicht erwartet sondern der PortableSigner.jar?

LG
Daniel
Benutzeravatar
snafu
User
Beiträge: 5966
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Freitag 23. März 2012, 15:07

Evtl macht es sogar Sinn, für deine Abfrage Jython zu nutzen? Damit kannst du Java-Bibliotheken innerhalb von Python importieren und dementsprechend direkt auf deren Klassen zugreifen - sofern eine Installation auf dem System (oder den Systemen), wo dein Skript zum Einsatz kommt, denn möglich ist.

Ansonsten gibt es halt `subprocess.check_output()`. Dort musst du die Argumente zum Aufruf als Liste übergeben (kennst du ja jetzt schon) und erhälst die Programmausgabe als String zurück. Bei einem Fehler im benutzten Programm (resp. Errorcode des Programms != 0) wird automatisch eine Exception von Python geworfen.

Und ja, wenn die Batchdatei im Prinzip nur die Zeile für den Programmaufruf beinhaltet, dann kannst du das natürlich auch direkt über Python ausführen lassen, ohne den Umweg zu gehen. An der Batchdatei selbst dürfte sich ja vermutlich so schnell nichts ändern.

Und sofern es dir zu umständlich ist, dein Kommando in einzelne Häppchen zu zerstückeln, bietet sich shlex.split(dein_kommando) an.
Benutzeravatar
snafu
User
Beiträge: 5966
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Freitag 23. März 2012, 15:24

mcdaniels hat geschrieben:Ziel ist es, dass *.pdf Dateien aus einem Verzeichnis eingelesen werden (Liste). Danach soll mittels einem PDF Signer (Portable Signer) die PDF Datei signiert werden.
Passiert denn noch irgendwas darüber hinaus? Also sozusagen etwas pythonspezifisches? Bisher sieht dein Code nämlich so aus, als ahmst du Shellprogrammierung mit Python nach. Nicht mal das: Du rufst Shellprogramme (wie `copy` und `del`) über Python auf, wo Python für Dateioperationen usw reichlich Funktionalität mitbringt und dafür nicht auf externe Programme angewiesen ist. Da möchte man in der Tat die Hände über den Kopf zusammenschlagen.

Bist du denn sicher, dass deine Aufgabenstellung nicht besser als reine Batchdatei gelöst ist? Denn nur weil man Python auch dafür benutzen *kann*, muss man es ja nicht zwangsläufig aucht tun, wenn's die Sache eigentlich viel umständlicher macht. ;)
Benutzeravatar
/me
User
Beiträge: 3276
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Freitag 23. März 2012, 15:33

mcdaniels hat geschrieben:Stimmt, das hatte ich nicht bedacht, was rstrip angeht. Danke für den Hinweis. Könntest du eventuell das mit den Argumenten noch präzisieren?
Ein Punkt ist: '-o' ist nicht gleich ' -o '.
Antworten