path variable testen (bash)

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
RedSharky
User
Beiträge: 99
Registriert: Donnerstag 13. April 2006, 15:38

Hallo,

ich möchte testen, ob ein externes Programm über eine Path Variable angesprochen und ausgeführt werden kann (ansonsten soll der User einen validen Pfad angeben).

Ich habe es mit os.access, os.path.isfile ausprobiert; beides immer False.

Man man bräuchte ein Python-Äquivalent von `which`, z.B.: `which mybinary`
/usr/bin/mybinary

Mir fällt bis jetzt nur ein, ein Shell-Commando abzusetzen, aber das ist doch doof, oder?

Danke!
deets

Wieso ist es doof, ein Shell-Befehl auszufuehren, wenn du danach eben einen Shell-Befehl ausfuehren willst? Du verlaesst dich auf die Semantik eines Subprozesses, und wie dessen Aufruf mit der PATH-Variable umgeht. Dann benutzt das doch.

Externe Kommandos sind nur dann "doof", wenn man ihre Existenz nicht voraussetzen kann (und "which" sollte es geben...), oder das was sie tun zu Ineffizienz fuehrt ("sort" aufrufen, um eine Liste von Strings zu sortieren... oder sowas).
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@deets: Ich sehe jetzt nicht wirklich, wo etwas von "Shell-Befehl absetzen" steht. Die Aufgabenstellung scheint eher zu sein, ob zu einem angegebenen Programmnamen die passende ausführbare Datei im `PATH` gefunden wird und zwar ohne ein tatsächliches Ausführen des Programms zu probieren. Da kann man IMHO durchaus sagen, dass man es plattformunabhängig halten will und `which` lieber vermeidet.

Mein Vorschlag:

Code: Alles auswählen

import os

def which(command):
    basedirs = os.getenv('PATH', '').split(os.pathsep)
    for basedir in basedirs:
        path = os.path.join(basedir, command)
        if os.path.isfile(path) and os.access(path, os.X_OK):
            return path
Wobei der zusätzliche Test auf Ausführbarkeit AFAIK beim "normalen" `which` nicht besteht.
deets

@snafu:

Das war genau das was ich meinte: du hast jetzt etwas implementiert, von dem du *glaubst*, es ist das richtige Verhalten. Ich bezweifele nicht, dass es dem "Original" recht nahe kommt. Aber wissen wir's? Nee. Ob da zB Pfad-Normalisierung dabei sind usw.

So wie du zB 100%ig nicht gewusst hast, dass "which" die Ausfuehrbarkeit bereits testet.

Wo ist also der Gewinn von deinen 7 Zeilen Code gegen ein simples

Code: Alles auswählen

if subprocess.call(["wich", command) == 1:
   print "Kommando", command, "existiert nicht"
?
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

deets hat geschrieben:Wo ist also der Gewinn von deinen 7 Zeilen Code gegen ein simples ... ?
Man kann `which' nicht falsch schreiben ;)

Fuer mich sieht es aber so aus, als ob der OP einfach den Fall abfangen wird, dass ein Programm nicht gefunden wird und dann den User befragt. Was spricht hier gegen ein Abfangen des `OSError`?
deets

Tippfehler gilden nicht...

Natuerlich kann man den OSError abfangen, aber zum einen ist der unspezifischer - welcher genau ist jetzt der richtige, bzw. koennen welche fliegen, die nichts mit der nicht-existenz des Kommandos zu tun haben?

Und so wie ich den OP verstanden habe, geht es eher um sowas wie ne Konfiguration. Man will *vorher* wissen, ob's was bestimmtes gibt, und wenn nicht, dann muss der User das erstmal richtig konfigurieren. Und weil nicht jedes Kommando seiteneffektfrei daher kommt, finde ich das which (mit 2 h) auch ok.
lunar

@deets "which" ist eine ziemlich dumme Idee. Man kann sich nämlich weder auf das Ausgabeformat noch überhaupt auf die Existenz dieses Kommandos verlassen. Um "which" zu verwenden, ob die Existenz eines Kommandos zu prüfen, müsste man also erstmal prüfen, ob "which" existiert. Du verstehst?

Auf der anderen Seite ist in POSIX ziemlich klar spezifiziert, wie "$PATH" zu interpretieren und wie Kommandos über "$PATH" aufzulösen sind. Dort kann man nachlesen, dass die Prozedur ziemlich einfach ist, und insbesondere keine „Pfad-Normalisierung“ vorsieht. snafus Implementierung ist zwar nicht richtig, aber nahe dran:

Code: Alles auswählen

def is_command(command):
    return all(s not in command for s in filter(None, (os.sep, os.altsep)))

def resolve_command(command):
    if not is_command(command):
         raise ValueError(command)
    directories = (d or os.getcwd() for d in os.environ['PATH'].split(os.pathsep))
    candidates = (os.path.join(d, command) for d in directories)
    executables = (c for c in candidates if os.path.isfile(c) and os.access(c, os.X_OK))
    executable = next(executables, None)
    if not executable:
         raise LookupError(command)
    else:
         return command

def find_executable(command):
     if is_command(command):
           return resolve_command(command)
     else:
           return os.path.abspath(command)
Unter Windows ist das zwar ein bisschen komplizierter, weil "$PATHEXT" zu berücksichtigen ist, doch Windows hat ohnehin kein "which".

Ich persönlich würde das aber einfach sein lassen, und OSError abfangen, wie cofi vorgeschlagen hat. Klar hast Du recht, der "OSError" kann vielleicht auch eine andere Ursache haben. Doch was immer die Ursache sein mag, die Auswirkungen sind dieselben: Das Kommando kann nicht ausgeführt werden.
deets

lunar hat geschrieben:@deets "which" ist eine ziemlich dumme Idee. Man kann sich nämlich weder auf das Ausgabeformat noch überhaupt auf die Existenz dieses Kommandos verlassen. Um "which" zu verwenden, ob die Existenz eines Kommandos zu prüfen, müsste man also erstmal prüfen, ob "which" existiert. Du verstehst?
Soso, eine ziemlich dumme Idee. Hast du ein Beispiel fuer ein real existierendes Unix auf dem das *nicht* existiert, oder ist das nur ein Argument um des Argumentes willen? Der OP scheint sich jedenfalls auf Unix zu beziehen, von Plattformunabhaengigkeit war erstmal nicht die Rede.

Und hast du in meinem Code-Snippet etwas von Ausgabe-verarbeitung gesehen? Ich verwende den Rueckgabewert...
Ich persönlich würde das aber einfach sein lassen, und OSError abfangen, wie cofi vorgeschlagen hat. Klar hast Du recht, der "OSError" kann vielleicht auch eine andere Ursache haben. Doch was immer die Ursache sein mag, die Auswirkungen sind dieselben: Das Kommando kann nicht ausgeführt werden.
Ich persoenlich halte *das* fuer eine ziemlich dumme Idee, denn schliesslich mag dieser OSError am Ende einer langen Kette von externen Programmaufrufen stehen, welche bis dahin schon eine Menge an Seiteneffekten produziert haben - und dann steht deine Anwendung in einem modalen Dialog oder ist abgebrochen, statt ueber Nacht deine Daten zu crunchen. Das haette *ich* dann gerne vorher gewusst

Aber dumme Ideen liegen ja auch im Auge des Betrachters.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Der Weg über `OSError` hätte halt den Nachteil, dass das Programm, wenn es existiert, dann auch erstmal ausgeführt wird, was unter Umständen nicht gewünscht ist (kenne die Rahmenbedingungen ja nicht).

Aber bevor ich mir irgendein höchst POSIX-konformes `which` bastle (und ich Wert darauf lage, dass dies erfüllt wird), würde ich wahrscheinlich auch lieber `which` direkt ausführen.

Mein eigener Ansatz mag jetzt nicht 1:1 übereinstimmen (mangels Kenntnisse über den Standard), aber deckt IMHO die eigentliche Problemstellung ganz gut ab.

Also ich würde ja sagen: Entweder ein nicht ganz perfektes nachgemachtes `which` oder eben ein "echtes" `which`, sofern davon ausgegangen werden kann, dass nur unixoide Plattformen genutzt werden, wo `which` mutmaßlich immer vorhanden ist. Alles andere wird mir persönlich zu absurd.
RedSharky
User
Beiträge: 99
Registriert: Donnerstag 13. April 2006, 15:38

Danke für die vielen fundierten Hinweise! Ich denke, ich werde doch "which" benutzen. Ausführen des Programmes will ich auf jeden Fall vermeiden, da es etwas bescheuert programmiert ist (hatte ich nicht erwähnt).
Danke nochmal!!!
Antworten