Das deutsche Python-Forum

Diskussionen rund um die Programmiersprache Python
Aktuelle Zeit: Fr Sep 03, 2010 02:07

Alle Zeiten sind UTC + 1 Stunde [ Sommerzeit ]




Ein neues Thema erstellen Auf das Thema antworten  [ 17 Beiträge ]  Gehe zu Seite 1, 2  Nächste
Autor Nachricht
 Betreff des Beitrags: subprocess, popen und Co.
BeitragVerfasst: Di Mär 01, 2005 12:27 
Moderator

Registriert: Di Aug 10, 2004 10:40
Beiträge: 6153
Wohnort: duisburg
Also irgendwie komme ich nicht weiter... Das neue subprocess ist immer noch ein Rätsel für mich und leider gibt es zu wenig Informationen darüber...

Also am liebesten möchte ich eine Art Shell-Objekt haben den ich ein Befehl schicken kann, Ausgaben auslesen und danach wieder ein neuen Befehl schicken/lesen... Also ungefähr so:
Code:
ShellObj = os.popen()
ShellObj.write("cd ..")
print ShellObj.read()
ShellObj.write("pwd")
print ShellObj.read()
ShellObj.close()


Das Problem ist, ich möchte einen Befehl Ausführen und danach wissen, wie in der Shell das aktuelle Verz. ist.

Also wenn ich z.B. os.popen("cd ..") ausführe, habe ich keine Möglichkeit mehr zu erfahren, in welchen Verz. man sich jetzt befindet. Denn es wird der Befehl ausgeführt und fertig.
Die einzige Möglichkeit ist die Verkettung von Befehlen, aber das ist super unpraktisch... (Also z.B. "cd ..;pwd")




Außerdem hab ich festgestellt, das nicht wirklich eine PIPE (so wie ich es verstehe) zum Prozess existiert.
Hier mal ein Beispiel:
Code:
import subprocess
command = "python SleepPrint.py"
process = subprocess.Popen( command, shell=True, stdout=subprocess.PIPE)

print process.stdout.read()

SleepPrint.py:
Code:
import time

for i in xrange(5):
    print i
    time.sleep(0.3)


Man sieht nicht nach und nach das Aufzählen, sondern das Skript SleepPrint.py wird erst vollständig ausgeführt und danach sieht man die Ausgaben...
Und mein Verständnis von eine PIPE ist es, das ich direkt sehe, wie die print-Ausgaben gemacht werden...

Bin ich da auf dem falschen Dampfer?


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: Di Mär 01, 2005 14:53 
Administrator
Benutzeravatar

Registriert: Fr Jun 20, 2003 17:30
Beiträge: 13867
read() liest ja bis zum EOF, und das wird erst erreicht wenn das Script fertig ist. Also müsste man Bytes einlesen (read(1) für ein Byte)... tja, das geht aber bei mir bei meinen Tests auch nicht besser :(

_________________
My god, it's full of CARs!


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: Di Mär 01, 2005 23:26 
Moderator

Registriert: Mi Jan 26, 2005 00:29
Beiträge: 11277
Das wird sein Problem nicht lösen. Er ist ein wenig auf dem falschen Dampfer. Das Betriebssystem und/oder die C Bibliothek und/oder Python schreiben nicht jedes Byte sofort, sondern puffern die Daten. Das bedeutet die Daten gehen nur über die Pipe wenn der Puffer voll ist, oder wenn Du explizit verlangst, dass die Daten im Puffer rausgeschrieben werden sollen. Das wird bei Skriptende natürlich automatisch gemacht.

Setz hinter Dein ``print i`` doch mal ein ``sys.stdout.flush()`` und schau ob Du die Daten dann "live" siehst.


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: Mi Mär 02, 2005 09:20 
Moderator

Registriert: Di Aug 10, 2004 10:40
Beiträge: 6153
Wohnort: duisburg
BlackJack hat geschrieben:
Setz hinter Dein ``print i`` doch mal ein ``sys.stdout.flush()`` und schau ob Du die Daten dann "live" siehst.

Jep, das geht! Wobei man kann auch eine Puffergröße mit bufsize bei subprocess() angeben. Ich hab es mit verschiedenen größen versucht, es scheint überhaupt keine Auswirkung zu haben...
Naja, egal... Es funktioniert auch bei "chkdsk c:", sodas man "zwischen Ergebnisse" sieht...
Kann man davon ausgehen, das es je nach Befehl funktioniert oder nicht, abhändig davon ob flush gemacht wird oder nicht?
Das ganze soll mit mein console.py unter Linux laufen...

Im Übrigen kann man statt read(1) besser readline() verwenden. Aber wie eine Schleife aufbauen? Geht das anders besser:
Code:
while 1:
    line = child_stdout.readline()
    if line == "": break
    sys.stdout.write( line )


Noch jemand eine Idee mit dem "offenhalten" der Shell für einen weiteren Befehl??? Ein child_stdin.write("dir") nach der while-Schleife führt zum Fehler: "IOError: [Errno 22] Invalid argument"


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: Do Mär 03, 2005 19:04 
Moderator

Registriert: Di Aug 10, 2004 10:40
Beiträge: 6153
Wohnort: duisburg
Kann mir da keiner wieter helfen, oder auch sagen das es nicht geht???


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: Fr Mär 04, 2005 02:13 
Moderator

Registriert: Mi Jan 26, 2005 00:29
Beiträge: 11277
jens hat geschrieben:
BlackJack hat geschrieben:
Setz hinter Dein ``print i`` doch mal ein ``sys.stdout.flush()`` und schau ob Du die Daten dann "live" siehst.

Jep, das geht! Wobei man kann auch eine Puffergröße mit bufsize bei subprocess() angeben. Ich hab es mit verschiedenen größen versucht, es scheint überhaupt keine Auswirkung zu haben...


Es hat keine Auswirkungen darauf was das gestartete Programm macht. Ich schrieb ja extra es kann auch beim Betriebssystem und/oder in den Programmen selbst gepuffert werden.

Zitat:
Kann man davon ausgehen, das es je nach Befehl funktioniert oder nicht, abhändig davon ob flush gemacht wird oder nicht?


Jep, davon kannst Du ausgehen.

Zitat:
Im Übrigen kann man statt read(1) besser readline() verwenden. Aber wie eine Schleife aufbauen? Geht das anders besser:
Code:
while 1:
    line = child_stdout.readline()
    if line == "": break
    sys.stdout.write( line )


Code:
for line in child_stdout:
   sys.stdout.write(line)

# oder

sys.stdout.writelines(child_stdout)



Zitat:
Noch jemand eine Idee mit dem "offenhalten" der Shell für einen weiteren Befehl??? Ein child_stdin.write("dir") nach der while-Schleife führt zum Fehler: "IOError: [Errno 22] Invalid argument"


Wenn Du eine Shell steuern willst, dann musst Du auch eine Shell starten. Der Parameter `shell` sagt nur ob das Kommando über eine Shell laufen soll oder direkt.


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: Sa Mär 05, 2005 00:00 
Moderator

Registriert: Di Aug 10, 2004 10:40
Beiträge: 6153
Wohnort: duisburg
BlackJack hat geschrieben:
Wenn Du eine Shell steuern willst, dann musst Du auch eine Shell starten. Der Parameter `shell` sagt nur ob das Kommando über eine Shell laufen soll oder direkt.


Jep, das wird es sein... Zumindest unter Windows kann ich so zwei Befehle ausführen lassen:
Code:
cmd = "cmd.exe"

p = subprocess.Popen(cmd, shell=True, #bufsize=1,
          stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

p.stdin.write("dir c:\n")
p.stdin.write("dir d:\n")
p.stdin.close()

print p.stdout.read()
 

Allerdings kann man von stdout erst nach dem stdin.close() lesen ;( Ich denke, weil die shell halt noch auf Befehle wartet, ist kein EOF vorhanden...

Unter Linux weiß ich allerdings nicht, welche Entsprechung cmd = "cmd.exe" hat... Mit "sh", "/bin/sh" und "/sbin/sh" bekomme ich immer die Fehlermeldung:
Code:
: bad interpreter: Datei oder Verzeichnis nicht gefunden


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: Sa Mär 05, 2005 12:13 
Administrator
Benutzeravatar

Registriert: Fr Jun 20, 2003 17:30
Beiträge: 13867
jens hat geschrieben:
Unter Linux weiß ich allerdings nicht, welche Entsprechung cmd = "cmd.exe" hat... Mit "sh", "/bin/sh" und "/sbin/sh" bekomme ich immer die Fehlermeldung:
Code:
: bad interpreter: Datei oder Verzeichnis nicht gefunden

/bin/bash
/bin/ksh
usw.
oder /usr/bin/env bash, so wie in Python Scripten.

_________________
My god, it's full of CARs!


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: Sa Mär 05, 2005 14:58 
Moderator

Registriert: Di Aug 10, 2004 10:40
Beiträge: 6153
Wohnort: duisburg
Aha... Es steckt is os.environ["SHELL"]... Bei mir ist es /bin/bash
Nun klappt es...

Viel hab ich durch die ganze Aktion allerdings nicht gewonnen... Denn in Prinzip konnte man das auch schon mit subprocess.Popen( "ls;pwd" ) erreichen... Denn schade ist, das man eigentlich nicht weiß wo die Ausgabe des ersten Befehls aufhöhrt und wo der zweite Befehl anfängt :( Mit ging es ja ursprünglich darum herraus zu finden, in welchem Verzeichnis man durch irgend ein Befehl gelandet ist...

Code:
cmd = "cd /test"

p = subprocess.Popen(os.environ["SHELL"], shell=True,
          stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

p.stdin.write( "%s\n" % cmd )
p.stdin.write( "pwd\n" )
p.stdin.close()

txt = p.stdout.read()
txt = txt.split()
print "-"*80
print "\n".join(txt[:-1])
print "-"*80
print "Aktuelles Verz.:", txt[-1]
print "-"*80


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: Sa Mär 05, 2005 23:29 
Moderator

Registriert: Mi Jan 26, 2005 00:29
Beiträge: 11277
Vielleicht kann Pexpect das was Du willst. Damit kann man angeblich Kommandozeilenprogramme in der Art steuern wie Du das möchtest.


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: Mi Mär 09, 2005 00:57 
Moderator

Registriert: Mi Jan 26, 2005 00:29
Beiträge: 11277
Ich habe mir Pexpect mal angeschaut. Sowas hier funktioniert:

Code:
#!/usr/bin/env python
import pexpect

command = 'ls --color'
shell_prompt = r'sh-(.*)\$'

shell = pexpect.spawn('sh')
shell.expect(shell_prompt)
shell.sendline(command)
shell.expect(shell_prompt)
command_output = shell.before
shell.sendline('pwd')
shell.expect(shell_prompt)
shell.sendeof()
working_directory = shell.before.split('\n')[-2]

print command_output
print '$PWD =', working_directory


Eventuell musst Du den regulären Ausdruck für den Shell-Prompt anpassen.

Aber ich stelle mir die Frage, was Du machen willst wenn jemand zum Beispiel einfach nur ``cat`` eingibt!?


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: Mi Mär 09, 2005 09:12 
Moderator

Registriert: Di Aug 10, 2004 10:40
Beiträge: 6153
Wohnort: duisburg
Ich hab mich dazu entschieden, die "cd" Befehle abzufangen und selbst auszuführen... Somit weiß ich in welchem Verz. man sich befindet... Wenn nun Programme eigenständig das Verz wechseln, dann bekomme ich davon natürlich nichts mit... Aber ich kenne spontan eh keins was das macht.

Mit "cat" oder auch "top" gibt es natürlich Probleme... Wäre zu überlegen, ob man nicht die Laufzeit der gestarteten Programme einschränken könnte... Denn die laufen ja weiter... Wenn man so ein Programm gestartet hatte, kann man den Server auch nicht richtig beenden...

Müßte ich dazu was mit Threading machen? Damit kenne ich mich noch überhaupt nicht aus...


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: Mi Mär 09, 2005 14:40 
Administrator
Benutzeravatar

Registriert: Fr Jun 20, 2003 17:30
Beiträge: 13867
Du könntest es als Threads starten, deren Lebenszeit kannst du auch beeinflussen, so wies es im Cookbook steht. Allgemein ist das Kapitel über Threads sehr nützlich.

Was mich interessieren würde, sind noch kleinere Threads, sogenannte greenlets, denn Threads sind manchmal problematisch.

_________________
My god, it's full of CARs!


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: Do Mär 10, 2005 14:27 
Moderator

Registriert: Di Aug 10, 2004 10:40
Beiträge: 6153
Wohnort: duisburg
OK, ich hab mir hier mal was zusammen gebasteln, um zu testen, wie man eine Art "Timer" bei subprocess realisieren kann:
Code:
import os, sys, threading, subprocess, time, signal


from optparse import OptionParser
OptParser = OptionParser()
options, args = OptParser.parse_args()

if len(args) != 0:
    # for-test-Schleife
    for i in xrange(10):
        print i
        sys.stdout.flush()
        time.sleep(0.2)
    print "for-test-Schleife abgelaufen!"
    sys.exit()



class MyThread(threading.Thread):
    def __init__( self, command, *args, **kw):
        self.command = command
        threading.Thread.__init__(self,*args,**kw)

    def readOutData( self, readObj ):
        "Ausgaben vom subprocess ausgeben"
        while 1:
            line = readObj.readline()
            if line == "": break
            sys.stdout.write( line )

    def run(self):
        "Führt per subprocess einen den Befehl 'self.command' aus."
        print "Starte '%s'..." % self.command,
        self.process = subprocess.Popen(
                self.command,
                shell=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE
            )
        print "OK"
        print "Lese Ausgaben:"
        print "-"*80
        self.readOutData( self.process.stdout )
        self.readOutData( self.process.stderr )
        print "-"*80

    def stop( self ):
        """
        Testet ob der Prozess noch läuft, wenn ja, wird er mit
        os.kill() (nur unter UNIX verfügbar!) abgebrochen.
        """

        if self.process.poll() != None:
            print "Prozess ist schon beendet"
            return

        print "Prozess abbrechen...",
        os.kill( self.process.pid, signal.SIGQUIT )
        print "OK"


test=MyThread( "python %s test" % __file__ )
test.start()    # thread starten
test.join(1)    # 1sek laufen lassen
test.stop()     # Prozess abbrechen

print "Fertig!"


Das Skript ruft per subprocess sich selber mit einem Parameter ("test") auf. Damit trifft die obere if-Anweisung zu und es wird eine for-Schleife gestartet, die einfach eine Zahl ausgibt...
Die for-test-Schleife dauert allerdings länger, als die 1sek. die mit "join(1)" festgelegt wurde. Somit wird stop() ausgeführt und der Prozess abgebrochen.


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: Mi Aug 10, 2005 20:04 
Moderator

Registriert: Di Aug 10, 2004 10:40
Beiträge: 6153
Wohnort: duisburg
Da ich genau die selbe Funktion für PyLucid benötige, hab ich nochmal das ganze überarbeitet:
Code:
class subprocess2(threading.Thread):
    """
    Allgemeine Klasse um subprocess mit einem Timeout zu vesehen.

    Da os.kill() nur unter Linux und Mac verfügbar ist, funktioniert das
    ganze nicht unter Windows :(
    """

    def __init__( self, command, cwd, timeout ):
        self.command    = command
        self.cwd        = cwd
        self.timeout    = timeout

        self.killed = False # Wird True, wenn der Process gekillt wurde
        self.out_data = "" # Darin werden die Ausgaben gespeichert

        threading.Thread.__init__(self)

        self.start()
        self.join( self.timeout )
        self.stop()

        # Rückgabewert verfügbar machen
        self.returncode = self.process.returncode

    def run(self):
        "Führt per subprocess den Befehl 'self.command' aus."
        self.process = subprocess.Popen(
                self.command,
                cwd     = self.cwd,
                shell   = True,
                stdout  = subprocess.PIPE,
                stderr  = subprocess.STDOUT
            )

        # Ausgaben speichern
        while 1:
            line = self.process.stdout.readline()
            if line == "": break
            self.out_data += line

    def stop( self ):
        """
        Testet ob der Prozess noch läuft, wenn ja, wird er mit
        os.kill() (nur unter UNIX verfügbar!) abgebrochen.
        """

        if self.process.poll() != None:
            # Prozess ist schon beendet
            return

        self.killed = True
        os.kill( self.process.pid, signal.SIGQUIT )


Beispiel Aufruf:
Code:
    import os, subprocess, threading, signal
   
    process = subprocess2( "top", "/", timeout = 2 )
   
    if process.killed == True:
        print "Timout erreicht! Prozess wurde gekillt."
    print "Exit-Status:", process.returncode
    print "Ausgaben:", process.out_data

_________________

- Python CMS: http://www.pylucid.org
- Mein ohloh Profil


Nach oben
 Profil  
 
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 17 Beiträge ]  Gehe zu Seite 1, 2  Nächste

Alle Zeiten sind UTC + 1 Stunde [ Sommerzeit ]


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.

Suche nach:
Gehe zu:  
cron
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
Deutsche Übersetzung durch phpBB.de