Mit subprocess zugewiesene Variable exportieren

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
Raspx10
User
Beiträge: 7
Registriert: Freitag 10. Juli 2015, 15:15

Hallo miteinander,
ich arbeite mich gerade in das Modul subprocess ein, da man os.system ja am besten nicht mehr verwenden sollte. Nun bin ich (wieder) auf ein Problem gestoßen, die Forum und Internet-Suche hat mir keine direkte Lösung gebracht.
Und zwar habe ich folgende Funktion definiert:

Code: Alles auswählen

import subprocess
def getMyIP():
      x = subprocess.Popen("Linux Befehl", shell=True, stdout=subprocess.PIPE)
      stdoutdata,stderrdata = x.communicate()
      return stdoutdata
Diese Funktion speichert mir meine Konsolenausgabe in stdoutdata (in dem Fall meine IP-Adresse) und gibt diese zurück.
Gebe ich stdoutdata in der gleichen Python Datei aus, so wird das richtige angezeigt (IP-Adresse). Binde ich nun die Python Datei in eine andere Python Datei und importiere die Funktion, so gibt sie folgendes aus:
<function getMyIP at 0x76a7c430>
Ausgabe erfolgt ganz normal:

Code: Alles auswählen

import lib_mit_getMyIP
IP = lib_mit_getMyIP.getMyIP()
print IP
Hab mir die letzten Tage den Kopf darüber zerbrochen. Ich habe nun alle Varianten von subprocess ausprobiert, bisher kein Erfolg. Hoffe ihr könnt mir helfen.
Vielen Dank schonmal
Raspx10
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Das hat nichts mit subprocess zu tun (obwohl du `shell=True` vermeiden solltest, oft ist es nicht nötig extra eine Shell zu starten). Kann es sein, dass du die "Klammern" bei dem Aufruf deiner Funktion vergessen hast oder die Datei, welche die Funktion enthält, zufällig getMyIP heißt?
the more they change the more they stay the same
BlackJack

@Raspx10: Ich rate einfach mal das das nicht der tatsächliche Code ist. Bitte zeige was Du *wirklich* ausführst, sonst müssen wir hier wild herum raten wie denn nun der Code zu Deinem Problem aussehen mag. Das sollte eigentlich anders laufen. ;-)

Das ``shell=True`` muss aus dem Aufruf da übrigens noch rausnehmen, denn sonst hast Du Dir die Gründe warum man `os.system()` nicht verwenden sollte, auch bei `subprocess` wieder eingefangen.
Raspx10
User
Beiträge: 7
Registriert: Freitag 10. Juli 2015, 15:15

Hallo,
danke für die schnelle Antwort! Das Problem lag wirklich an den Klammern, die hatte ich hier im Forum, aber nicht im Code :oops: Das werde ich jetzt nie wieder vergessen :wink:
Ok, das mit der Shell war eine Notlösung, weilbei langen Ausdrücken fällt mir sowas echt schwer es korrekt einzutippen. Oder mit Zeichen wie z.B. "\cp".
So sieht nämlich mein Code aus:

Code: Alles auswählen

import subprocess
def getMyIP():
      x = subprocess.Popen("/sbin/ifconfig eth0 | sed -n '/addr:/s/ [^r]*..//gp'", shell=True, stdout=subprocess.PIPE)
      stdoutdata, stderrdata = x.communicate()
      return stdoutdata
Hierbei stellt sich auch die Frage: Was macht man, wenn man das '/addr:/s/ [^r]*..//gp' korrekt übergeben möchte? Also doppelt ' geht ja bestimmt nicht? Da finde ich es wirklich einfacher alles in " zu setzen :? So wirklich gute Quellen habe ich dazu bisher auch noch nicht gefunden :/

Wünsche euch noch einen schönen Abend!
Raspx10
BlackJack

@Raspx10: Man kann \ zum escapen benutzen aber letztendlich würde ich da keine Pipe in der Shell starten um ``sed`` zu starten für etwas was man auch in Python lösen kann.
Raspx10
User
Beiträge: 7
Registriert: Freitag 10. Juli 2015, 15:15

Was bedeutet genau "\ zum escapen benutzen"?
Ich habe es auch anfangs damit versucht:

Code: Alles auswählen

import socket
sys_name=socket.gethostname()
ip_addr=socket.gethostname(sys_name)
print ip_addr
Aber das läuft nur unter Windows, unter Raspbian (Linux) liefert er mir nur 127.0.0.1
Oder kennst du noch eine andere Möglichkeit?
BlackJack

@Raspx10: Das escapen von Anführungszeichen ist gleich das erste was im Tutorial im Abschnitt Strings erklärt wird.

Ansonsten findet man im Netz auch Code um die IP-Adresse zu einem Interface per `ioctl()`-Aufruf abzufragen:

Code: Alles auswählen

import fcntl
import socket
import struct


def get_ip_address(interface_name):
    return socket.inet_ntoa(
        fcntl.ioctl(
            socket.socket(socket.AF_INET, socket.SOCK_DGRAM).fileno(),
            0x8915,  # SIOCGIFADDR
            struct.pack('256s', interface_name[:15])
        )[20:24]
    )
Raspx10
User
Beiträge: 7
Registriert: Freitag 10. Juli 2015, 15:15

Hallo BlackJack,
danke für deine Antwort. Diesen Code habe ich im Netz auch schon gefunden und erfolgreich ausprobiert. Nur fand ich ihn nicht sehr verständlich im Vergleich zu meinem ersten Code mit "sed" :?
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Alternativ lässt du `sed` einfach weg und implementierst den Teil einfach in Python.

Einfach `/sbin/ifconfig eth0 ` aufrufen und aus der Ausgabe mit Python die IP rausziehen.
the more they change the more they stay the same
BlackJack

@Raspx10: Ob nun verständlicher oder nicht, das ist der direkte Weg so wie das ``ifconfig`` sehr wahrscheinlich intern auch macht. Würde ich dem starten von *drei* externen Programmen (Shell, ``ifconfig``, und ``sed``) vorziehen wo eines dann unter anderem auch genau das macht, die Information dann mit vielen anderen in Text ausgibt, aus dem man sie dann mühsam wieder rausparsen muss.
Raspx10
User
Beiträge: 7
Registriert: Freitag 10. Juli 2015, 15:15

Ok, ich habe nun die ganze Zeit herumprobiert, den Befehl ohne die Shell einzubinden. Sehe das jetzt mal als Übung, danach werde ich dann wie vorgeschlagen schrittweise auf Python ausweichen. Und zwar habe ich den ifconfig und den sed Teil erst einmal getrennt, weil die zwei Befehle ja miteinander verkettet waren.

Code: Alles auswählen

import subprocess
# Ersteinmal die ganzen Infos von eth0 abspeichern
x = subprocess.Popen([r"/sbin/ifconfig", 'eth0'],stdout=subprocess.PIPE)
#Dann den sed Befehl anwenden
y = subprocess.Popen(['sed', '-n', r"\'/addr:/s [^r]*..//gp\'"], stdin=x.stdout, stdout=subprocess.PIPE)
Habe hier auch das escapen der ' mit \ angewendet und außerdem das rawinput für / benutzt. Er spuckt mir aber den Fehler " sed -e expression #1, char 25: unterminated address regex" aus :?
Mir ist auch noch nicht ganz klar was " [^r]*.." in dem sed-Befehl bedeutet? Also der sed Befehl ersetzt in der Zeile mit "addr" alle " [^r]*.." durch nichts (//), das glaube ich bisher herausgefunden zu haben :)
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@Raspx10: Du fragst jetzt ernsthaft, was sed macht?
Raspx10 hat geschrieben:[...] Nur fand ich ihn nicht sehr verständlich im Vergleich zu meinem ersten Code mit "sed"
Die Anführungszeichen sind nötig, damit die Shell das Argument als ein Argument erkennt. Ohne zwischengeschaltete Shell sind sie aber überflüssig. Ansonsten ist der Ausdruck auch ein anderer als in Deinem ersten Beispiel.
Raspx10
User
Beiträge: 7
Registriert: Freitag 10. Juli 2015, 15:15

@Sirius3: Ich habe das Gefühl, du hast meine Fragestellung nicht richtig verstanden? Ich finde den sed-Befehl verständlichER.

Ich habe gelesen, dass man für Verzeichnisse die Anführungszeichen verwendet, damit nichts falsch gedeutet wird mit den Slash Zeichen.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Was die Leute immer so lesen ...

@Raspx10: solange die Commandozeile von einer Shell interpretiert wird, mag das stimmen, hier nicht.
Raspx10
User
Beiträge: 7
Registriert: Freitag 10. Juli 2015, 15:15

Ok, danke für deine Antwort. Es hat funktioniert, nun würde ich versuchen, alles in Python zu lösen. Aber dazu muss ich immer noch wissen, was der Ausdruck "[^r]*.." genau beschreibt. Weißt du da auch etwas darüber?
BlackJack

@Raspx10: Da weiss die Dokumentation von ``sed`` mehr drüber: sed, a stream editor.

Grundsätzlich könntest Du aber auch selber überlegen wie man das lösen könnte ohne eine ``sed``-Lösung 1:1 nach Python umzuschreiben.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Raspx10 hat geschrieben:

Code: Alles auswählen

import lib_mit_getMyIP
IP = lib_mit_getMyIP.getMyIP()
print IP
Wieso eigentlich `print`? Sicher, dass du nicht lieber ``subprocess.call(['echo', IP])`` verwenden möchtest...? :twisted:

Mal im Ernst: Ein Aufruf von `sed` hat normalerweise nichts in einem Python-Programm zu suchen. Python kann nämlich all das, was `sed` auch kann, da es reichlich Funktionalität zur Verarbeitung von Text anbietet. Außerdem ist jedes externe Tool ja eine weitere Abhängigkeit. Und Abhängigkeiten (d.h. alles was nicht zu Pythons Boardmitteln gehört) sollte man IMHO nur mitschleppen, wenn das Schreiben des nötigen Codes ohne die Abhängigkeit zu umfangreich wäre. In deinem Fall ist die Verarbeitung der Rückgabe des `ifconfig`-Befehls aber lediglich ein Zweizeiler.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

BlackJack hat geschrieben:Grundsätzlich könntest Du aber auch selber überlegen wie man das lösen könnte ohne eine ``sed``-Lösung 1:1 nach Python umzuschreiben.
Dem stimme ich zu. Anstatt die `sed`-Syntax zu entschlüsseln, kann man sich auch einfach die Ausgabe von ``ifconfig eth0`` ansehen und überlegen, wie man an den gewünschten Textausschnitt kommt. Dazu bedarf es einer schrittweisen Vorgehensweise. Mit den einzelnen Schritten dürfte man als Anfänger auch etwas länger beschäftigt sein. Wenn man da systematisch rangeht, wird man auch spezifische Fragen haben. Die Fragen, die man durch Recherche nicht selbst beantwortet bekommt, kann man dann gerne hier im Forum stellen.
Benutzeravatar
sparrow
User
Beiträge: 4164
Registriert: Freitag 17. April 2009, 10:28

Eine von dir in Python implementierte Lösung hätte für dich außerdem den Vorteil, dass du sie bei Bedarf flexibler machen könntest. Hier gibt es zum Beispiel kein eth0.
Antworten