Überprüfen ob Anwendung vorhanden

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.
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Hallo werte Python Community,

ich möchte überprüfen ob eine Anwendung, die ich über subprocess aufrufe, vorhanden ist.

Bsp.:

Code: Alles auswählen

p = subprocess.Popen("robocopy.exe")
Leider bekomm ich zum Beispiel unter Windows einen WindowsError ausgespuckt, falls die Datei nicht vorhanden ist:

Code: Alles auswählen

WindowsError: [Error 2] Das System kann die angegebene Datei nicht finden
Jetzt könnte ich natürlich diesen Fehler abfangen und dann entsprechend handeln. Leider ist das ja ein sehr betriebsystemspezifisches Verhalten.

Gibts da auch ne Möglichkeit das eleganter und portabler zu lösen?

Grüße,
anogayales
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Code: Alles auswählen

import os.path
os.path.exists("robocopy.exe")
deets

@numerix

Das ist ein bisschen zu kurz gegriffen. Weder Unix noch Windows fuehren nur Programme aus, die sich im current working directory befinden...

@anogayales

Ich denke schon, dass die Exceptions zu fangen der richtige Weg ist. Wenn du zB weisst, dass du dein Programm auf eine Art aufrufen kannst, die "harmlos" ist (also zB eine Versions-Abfrage oder sowas), dann ist das IMHO genau der richtige Weg.

Ausserdem sind Kommandozeilen-Tools eh sehr Systemspezifisch... robocopy.exe findet sich weder unter OSX noch Linux oder sonst einem OS ausser eben Windows. Also, warum die Sorge?
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Nun speziell in dem Fall macht es keinen Sinn, das Problem plattformunabhaengig zu loesen, weil `robocopy.exe` sowieso hoechstens unter Windows existiert.

Unter der Annahme, dass der `subprocess` Aufruf auch funktioniert, wenn sich `robocopy.exe` irgendwo im Programm-Suchpfad, greift numerix' Ansatz zu kurz und du muesstest den gesamten Pfad absuchen.

Edit: Zu langsam :(
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Die Dokumentation sagt, dass WindowsError eine Unterklasse von OSError ist. Dieser tritt z.B. unter OS/X auf. Also würde ich empfehlen zu hoffen, dass das unter Unix genauso ist und OSError abzufangen. Was für ein Fehler es war, kann man bei beiden Exceptions in "errno" sehen. In diesem Fall 2 = errno.ENOENT (no such file or directory).

Stefan
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Mein Codeschnipsel(chen) war nicht als fertige Lösung gedacht, sondern sollte nur ein Denkanstoß zur eigenen Beschäftigung des OP sein, in welche Richtung man es (auch) angehen könnte, wenn man es nicht über Exceptions lösen will. Ich würde es persönlich auch eher mittels exception handling angehen.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Naja, so sauber finde ich den Weg über eine Ausnahme nicht. Wenn man wirklich erstmal nur auf Existenz testen will, möchte man vielleicht nicht, dass ein existierendes Programm auch automatisch ausgeführt wird. Man sollte dann also besser den Pfad absuchen. Will man hingegen so oder so das Programm ausführen, dann sollte man ruhig die gezeigte Trial-and-Error Methode gehen, d.h. "auf doof" ausführen und im negativen Fall die Ausnahme behandeln.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Man kann auch erstmal einen "effektiven No-Op"-Aufruf machen, wie ``programmname --version`` oder sowas, bei dem das Programm nicht viel macht. Aber ja, vielleicht ist absuchen des Suchpfades besser, andererseits ist das gar nicht so einfach wie man denken könnte, weil gerade unter Windows alles mögliche ein per Subprocess aufrufbares Programm sein kann, aber nicht muss. Ich denke neben den üblichen EXE und COM-Dateien kann man wohl auch BAT und CMD-Dateien ausführen, sowie VBS, JS und nach Installation von Python auch PY-Dateien.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Naja, soll generell auf Ausführbarkeit einer Datei getestet werden oder im Kontext einer installierten Anwendung? Bei letzterem würde ich sagen, dass eine auf üblichem Weg installierte Anwendung im Regelfall auch im Suchpfad steht. Geht man nun davon aus, dass Dateinamen, für welche der Suchpfad greift, grundsätzlich auf ein ausführbares Programm zeigen, dann kann man sich das explizite Testen auf die tatsächliche Ausführbarkeit sparen. So mache ich das zumindest in Launchit mit der Funktion `is_command()`, weil eben dies in meinen Augen die Definition von "Kommando" ist.
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Vielen Dank für den Input:

Ich hab das mal ganz naiv implementiert, gibts irgendwelche verbesserungsvorschläge?

Code: Alles auswählen

def process_available(process_name):
    try:
        process = subprocess.Popen(process_name)
    except OSError as why:
        if why.errno == errno.ENOENT:
            return False
    return True
Grüße,
anogayales
BlackJack

@anogayales: Die Namensgebung ist IMHO falsch. Unter einem Prozess versteht man normalerweise etwas anderes. Und man startet hier ein Programm nur um zu sehen ob es da ist. Nicht alle Programme lassen sich auf diese Weise ohne Seiteneffekte starten. Ausserdem wird der Status nicht abgefragt — die Funktion erzeugt also ”Zombies”.
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Oh stimmt, Funktion müsste dann programm_available heißen. Über die Seiteneffekte bin ich mir bewusst. Meine Anwendung ruft nur solche Programme auf, die keine solchen Seiteneffekte haben. Werde ich aber dementsprechend dokumentieren.

Was meinst du mit Status abfragen?

Grüße,
anogayales
BlackJack

@anogayales: Prozesse haben einen numerischen Rückgabewert wenn das Programm beendet ist. Den bekommt man mit der `wait()`-Methode. Wenn man diesen Wert nicht abfragt, dann kann das Betriebssystem den Prozess nicht aus der Prozessliste entfernen. Diese "toten" aber noch existierenden Prozesse nennt man Zombies.
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Okay, alles klar. Was ist da das Vorgehen?

Falls der Process noch nicht terminiert ist, einfach killen?

So ungefähr?

Code: Alles auswählen

def programme_available(programme_name):
    process = None    

    try:
        process = subprocess.Popen(programme_name)
    except OSError as why:
        if why.errno == errno.ENOENT:
            return False

    if process is not None and process.wait() is None:
       process.kill()

    return True
Danke für die Hilfe,
anogayales
Zuletzt geändert von anogayales am Donnerstag 12. Mai 2011, 12:29, insgesamt 1-mal geändert.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

`.kill()` erscheint mir ziemlich unsinnig, wenn `.wait()` doch bereits den Returncode zurück gegeben hat. Denn wenn dieser Code abgefragt werden kann, ist das Programm bereits beendet.

Aber ich möchte nochmals zu bedenken geben: Ein probeweises Ausführen ist bei einem generischen Ansatz IMHO keine zufriedenstellende Lösung. Besonders in der jetzigen Form, wo nur ein Programmaufruf ohne Argumente angenommen wird, kann das doch sehr einschränkend für den Benutzer sein. Eine Option wie `harmless_arg='--help'` würde da schon mehr Freiheiten schaffen. Wobei wir ja jetzt von Windows sprechen, wo die Form bei Optionsangaben sich wieder unterscheiden kann. Naja, ist ja letztlich deine Entscheidung. Ich würd's halt anders machen.
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

snafu hat geschrieben:`.kill()` erscheint mir ziemlich unsinnig, wenn `.wait()` doch bereits den Returncode zurück gegeben hat. Denn wenn dieser Code abgefragt werden kann, ist das Programm bereits beendet.
Laut Doku für .wait() das einen Popen.returncode zurückgibt:
A None value indicates that the process hasn’t terminated yet
Anscheinend ist es also nicht so, dass das Programm aufjedenfall beendet worden ist, oder seh ich da was anderes?

Wie gesagt, in meinem spezifischen Fall haben Programme, die keine Parameter übergeben bekommen, keine Wirkung.

Grüße,
anogayales
BlackJack

@anogayales: Überleg mal was `wait()` auf deutsch heisst und was das wohl bedeuten mag…
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Ja, du hast doch selbst gesagt, dass ich den Status abfragen muss. Mir ist schon klar, das wait wartet bis der Kindsprocess terminiert ist. Deswegen ja die Anmerkung von snafu. Was bringt mir also der Returncode in Verbindung mit wait()? Brauch ich den dann überhaupt?

@BlackJack:
Wie finde ich nun diese Zombieprozesse und wie terminier ich die?
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

BlackJack hat geschrieben:@anogayales: Prozesse haben einen numerischen Rückgabewert wenn das Programm beendet ist. Den bekommt man mit der `wait()`-Methode. Wenn man diesen Wert nicht abfragt, dann kann das Betriebssystem den Prozess nicht aus der Prozessliste entfernen. Diese "toten" aber noch existierenden Prozesse nennt man Zombies.
Weil das evtl nicht ganz rueberkommt: Zombies haben schon terminiert. Der Rest steht im Quote.
BlackJack

@anogayales: Den Return-Code brauchst Du nicht, aber Du musst ihn halt "abholen", damit das Betriebssystem die Ressoucen von dem Prozess freigeben kann. Und das macht man mit `wait()`. Zombie-Prozesse musst Du nicht suchen und finden sondern dafür sorgen das sie nicht entstehen – eben zum Beispiel in dem Du `wait()` aufrufst.
Antworten