Subprocess Problem

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.
Antworten
ravenheart
User
Beiträge: 70
Registriert: Mittwoch 10. November 2010, 18:41

Hallo, nachdem os.system() deprecated ist, wollte ich eine Funktion schreiben, welche für mich das selbe tut.

Sprich, ich gebe einen String an, der dann wie in der Konsole ausgeführt wird.

shell_exec("cat test.txt | grep -v abc") zB

Leider haperts mit dem Ding noch etwas: ich bekomme anscheinend wahllos viele Leerzeilen am Schluss zu viel ausgegeben.

Wenn ich beispielsweise eine Textdatei mit folgendem Inhalt habe:
Lorem Ipsum
erhalte ich nach dem Aufruf von shell_exec("cat test.txt") folgendes Ergebnis
Lorem Ipsum

Mit 2 Leerzeilen am Schluss. Nunja, die Frage ist: Was muss ich ändern um dieses Verhalten zu vermeiden?
HIer der enstprechende Code:

Code: Alles auswählen

# -*- coding: utf-8 

### Imports ### 
import os 
import sys 
import shlex 
import subprocess 

def shell_exec(command): 
        cmds = command.split("|") 
        cmds = [ cmd.strip() for cmd in cmds] 
        cmds = map(shlex.split, cmds) 

        procs = [] 
        prev_out = sys.stdin 

        for cmd in cmds: 
                proc = subprocess.Popen(cmd, stdin=prev_out, stdout=subprocess.PIPE) 
                prev_out = proc.stdout 
                procs.append(proc) 

        (stdout, stderr) = procs[-1].communicate() 

        for proc in reversed(procs): 
                proc.wait() 

        print(stdout)
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

`os.system` ist nicht deprecated, sondern `subprocess` wird empfohlen.

Fuer dein Problem: http://docs.python.org/library/subproce ... -os-system

Wichtig is dabei das `shell=True`. Das musst du nicht selbst implementieren.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@cofi:

Wenn man aber die genannte Syntax nutzen möchte, ohne die Unsicherheiten einer implizit benutzten Shell zu haben, sollte man eben kein `shell=True` setzen, sondern es durchaus selbst implementieren.
ravenheart
User
Beiträge: 70
Registriert: Mittwoch 10. November 2010, 18:41

Also, ich hab das ganze ja meiner Meinung nach recht weit, nur dass ich irgendwie seltsame Ergebnisse bekomme in gewissen Fällen, bisher habe ich nur überflüssige Leerzeilen feststellen können.

Aber selbst das könnte in manchen Anwendungsfällen zu Problemen führen.

Konkret suche ich also nun eine Verbesserung meines Codes, so dass dieser stabil läuft.
Wichtig ist mir vor allem auch dass Pipes anwendbar sind.

Ich finde leider keinen Fehler, euer Rabenherz
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Und normalerweise nutzt man ``shell=False`` (was eh der Standardwert ist) eben weil man nicht so Programme wie grep reinhängen sollte sondern so "triviale" Funktionalität besser in Python löst.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
ravenheart
User
Beiträge: 70
Registriert: Mittwoch 10. November 2010, 18:41

Hmmm, dass ich höchstwahrscheinlich nicht grep verwenden möchte, ist eigentlich klar.
Es geht mir auch nicht darum, was ich mit damit jetzt tatsächlich anstelle. Es sei nur gesagt, dass es durchaus Sinn macht, diese Funktion zu haben.

Ich suche auch nicht nach Möglichkeiten Dinge, die ich über Shell oder Subprocess ablaufen lassen will, durch Python zu ersetzen.
Einzig und allein die Fehlerursache und vor allem die Behebung sind Sinn und Zweck des Threads.

Die verwendeten Funktionen sind hier so einfach wie möglich gehalten um gewünschte Funktionalität und Fehlerbeschreibung anzugeben.

Also nochmal wiederholen, bevor der Thread noch in eine von mir nicht gewünschte Richtung abschweift.

Ich möchte eine Funktion haben, welche aud subprocess basiert, welche einen String erwartet und dieses mehr oder weniger so ausführt, als wenn der String in der Konsole geschrieben worden wäre.

Mein Ansatz ist oben beschrieben, aber fehlerhaft.

Nun ist eine Korrektur erwünscht.

Danke!
(das ist keinesfalls ironisch oder böswillig gemeint)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Dann reicht mehr oder weniger ``subprocess.call``...
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
ravenheart
User
Beiträge: 70
Registriert: Mittwoch 10. November 2010, 18:41

Hmm, ich kann das gerade nicht testen,
aber wenn ich die Doc ansehen, scheint mir als wenn man Befehl und Argumente getrennt in einer Liste angeben müsste. Das wäre für meine Zwecke höchst ungeeignet ... naja eher sehr umständlich.

Weiterhin: wie sieht es da mit Pipes aus?
Ich kann leider im Beispiel nichts dergleichen erkennen.

Meinen Versuch habe ich an

Code: Alles auswählen

output=`dmesg | grep hda`
==>
p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]
von http://docs.python.org/library/subprocess.html
angelehnt.

Edit: ok, das mit dem Trennen zu einer Liste übernimmt shlex für mich, aber mit den Pipes sehe ich immer noch schwarz.

Um vielleicht das Fenster noch mehr einzuengen:

Es interessiert mich brennend, warum meine Variante nicht tut was sie soll.
Seek and destroy ... Wo liegt der (Denk) Fehler?
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ich würde das etwas kürzer schreiben:

Code: Alles auswählen

from shlex import split
from subprocess import PIPE, Popen

def runcmd(cmdline):
    proc = None
    for cmd in cmdline.split('|'):
        if not proc:
            # first process
            proc = Popen(split(cmd), stdout=PIPE)
        else:
            # further procs read from previous ones
            proc = Popen(split(cmd), stdin=proc.stdout, stdout=PIPE)

    output, errors = proc.communicate()
    return output
Gibt dann als Ergebnis:

Code: Alles auswählen

>>> with open('test.txt', 'w') as f: f.write('Lorem Ipsum\n')
>>> runcmd('cat test.txt')
'Lorem Ipsum\n'
Zu deiner Frage mit den Leerzeilen: Also wenn ich deine Funktion in IPython aufrufe, bekomme ich auch 2 Leerzeilen - eine von `print` und eine von IPython.
ravenheart
User
Beiträge: 70
Registriert: Mittwoch 10. November 2010, 18:41

Jo, danke . Ich hatte mich schon gewundert ,warum manchmal eine manchmal 2 Leerzeilen zu viel waren. Das lag daran, dass ich manchmal ipython verwendet hab und manchmal nicht.

Das Problem hab ich jetzt behoben , indem ich die print(,end="") funktion nehme.


Eine Sache, die ich gerne hätte, aber im Moment noch nicht zu verwirklichen weiß:

Code: Alles auswählen

echo hello > file.txt
Ich würde gerne auch solche Befehle ausführen können, nur wie kann ich diese abfangen?
Hierbei bin ich leider total überfragt, da ich ja unterscheiden müsste, in welchem Kontext das '>' auftritt.

Falls es hier eine einfache Möglichkeit gibt, ohne dass ich etwas wie einen Parser schreiben muss, wäre ich sehr erfreut.

TY
BlackJack

@ravenheart: Da wird wohl kein Weg drumherum führen. Wenn Du beliebige Eingaben dort verarbeiten können möchtest, hat Dein aufteilen an '|' ja sowieso schon ein Problem, zu Beispiel bei "echo '|foo|'", oder etwas komplexer: "xterm -e 'cat sig.txt | grep -i python; read'".
ravenheart
User
Beiträge: 70
Registriert: Mittwoch 10. November 2010, 18:41

hmm... gibt es so einen parser schon vorgefertigt?
BlackJack

@ravenheart: Die meisten Shells machen das schon für Dich. ;-)

Was ist denn überhaupt der Anwendungsfall? Warum musst oder willst Du Shell-Syntax selber parsen, anstatt einfach tatsächlich eine Shell zu benutzen?

Und wie weit soll das gehen? Was ist mit in der Shell eingebauten Kommandos wie zum Beispiel ``cd``? Oder Variablen (``cd $TEMP``)?
ravenheart
User
Beiträge: 70
Registriert: Mittwoch 10. November 2010, 18:41

Du hast Recht...
bevor ich mich jetzt in etwas hineinsteigere, sollte ich mir doch Gedanken darüber machen, ob das notwendig ist.

Das ganze ist nur dadurch entstanden, dass ich in der Python-doc gelesen habe, dass man os.system - Befehle besser ersetzt.

Für meinen Anwendungsfall ist es aber unerheblich, was ich letztendlich benutze, solange keine Fehler auftreten.

Ich denke ich belasse das ganze einfach mal so und entscheide je nach Situation, ob ich meine neue Funktion oder os.system nutze.

Dieses ganze Gefrickel lenkt mich nur von meiner eigentlichen Arbeit ab.
BlackJack

@ravenheart: Es gäbe ja noch den Mittelweg: Auf `os.system()` verzichten und `subprocess` verwenden, da aber eben über die Shell gehen. Also zum Beispiel ``subprocess.call("foo $WHATEVER | grep 'solution:' > result.txt", shell=True)``.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Dein Ansatz würde halt auf das Nachbauen der Shellfunktionalitäten hinauslaufen, z.B. eine Art bash.py ;)
Nun sind die Shells nicht über Nacht entstanden und bringen eine unüberschaubare Funktionsvielfalt mit. Selbst mit der Umsetzung eines Subsets der Funktionen dürftest Du eine Weile beschäftigt sein.

Das Unixkonzept lebt gerade von der Idee der kleinen Tools und Helferlein, die mit dem Schweizer Taschenmesser "Shell" ihre Wirkung entfalten können. Windows-Administratoren können hiervon ein Klagelied singen (gut mit der Powershell ist das dort besser geworden, allerdings wird die neue Syntax von vielen abgelehnt und die Funktionen liegen daher brach, ganz zu schweigen von den Unzulänglichkeiten des Windows-"Terminals").

@os.system:
Ich bin kein kategorischer Gegner von 'system()', allerdings sollte man die Fallstricke der Funktion kennen (siehe 'man system').

Shellscripting ist ein mächtiges Werkzeug in der unixoiden Welt. Manchmal ist es eben einfacher, die Shell und die unzähligen Systemtools zu benutzen, denn hierfür wurden die Tools kreiert. Man ist allzu schnell verleitet, diese Funktionen mit ellenlangem Code in der Programmiersprache der Wahl nachzubauen (Skriptsprachen wie Python oder Perl vorallem), wo die Shell mit einem Einzeiler aufwartet. Manchmal hilft es, nicht Python als "Zentralinstanz" zum Abarbeiten des Problems zu sehen, sondern die Shell hierfür zu nutzen und den Interpreter bei Bedarf hinzuzunehmen.

Die sicherheitskritische Betrachtung der Shellbenutzung hab ich mal ausser Acht gelassen. Sie ist durchaus ein Grund, die Funktionen teilweise nachbilden zu wollen, um ohne "echte" Shell auszukommen. Selbiges gilt für Plattformunabhängigkeit.
Antworten