Ich weiß, dass ich hier ein wenig wie ein Nekromant bin, aber ich bin auf diese Frage gestoßen und die akzeptierte Lösung hat für mich nicht in allen Fällen funktioniert. Ich dachte, es wäre trotzdem nützlich, sie einzureichen. Insbesondere die Erkennung des "ausführbaren" Modus und die Notwendigkeit, die Dateierweiterung anzugeben. Darüber hinaus funktionieren sowohl shutil.which
von python3.3 (verwendet PATHEXT
) als auch distutils.spawn.find_executable
von python2.4+ (versucht nur '.exe'
hinzuzufügen) nur in einem Teil der Fälle.
Also habe ich eine "Super"-Version geschrieben (basierend auf der akzeptierten Antwort und dem Vorschlag von Suraj zur Verwendung von PATHEXT
). Diese Version von which
erledigt die Aufgabe etwas gründlicher und probiert zuerst eine Reihe von "Breitphasen"-Breitensuchtechniken aus und versucht schließlich feingranuliertere Suchen im PATH
-Raum:
import os
import sys
import stat
import tempfile
def is_case_sensitive_filesystem():
tmphandle, tmppath = tempfile.mkstemp()
is_insensitive = os.path.exists(tmppath.upper())
os.close(tmphandle)
os.remove(tmppath)
return not is_insensitive
_IS_CASE_SENSITIVE_FILESYSTEM = is_case_sensitive_filesystem()
def which(program, case_sensitive=_IS_CASE_SENSITIVE_FILESYSTEM):
""" Simuliert das Unix 'which' Kommando. Gibt den absoluten Pfad zurück, wenn das Programm gefunden wurde """
def is_exe(fpath):
""" Gibt true zurück, wenn fpath eine ausführbare Datei ist, auf die wir zugreifen können """
accessmode = os.F_OK | os.X_OK
if os.path.exists(fpath) and os.access(fpath, accessmode) and not os.path.isdir(fpath):
filemode = os.stat(fpath).st_mode
ret = bool(filemode & stat.S_IXUSR or filemode & stat.S_IXGRP or filemode & stat.S_IXOTH)
return ret
def list_file_exts(directory, search_filename=None, ignore_case=True):
""" Gibt eine Liste von Tupeln (Dateiname, Erweiterung) zurück, die zum search_filename passen """
if ignore_case:
search_filename = search_filename.lower()
for root, dirs, files in os.walk(path):
for f in files:
filename, extension = os.path.splitext(f)
if ignore_case:
filename = filename.lower()
if not search_filename or filename == search_filename:
yield (filename, extension)
break
fpath, fname = os.path.split(program)
# ist ein Pfad: versuche direkten Programm-Pfad
if fpath:
if is_exe(program):
return program
elif "win" in sys.platform:
# kein Pfad: versuche fname im aktuellen Verzeichnis unter Windows
if is_exe(fname):
return program
paths = [path.strip('"') for path in os.environ.get("PATH", "").split(os.pathsep)]
exe_exts = [ext for ext in os.environ.get("PATHEXT", "").split(os.pathsep)]
if not case_sensitive:
exe_exts = map(str.lower, exe_exts)
# versuche Programm-Pfad pro Verzeichnis anzuhängen
for path in paths:
exe_file = os.path.join(path, program)
if is_exe(exe_file):
return exe_file
# versuche mit bekannten ausführbaren Erweiterungen pro Programm-Pfad pro Verzeichnis
for path in paths:
filepath = os.path.join(path, program)
for extension in exe_exts:
exe_file = filepath+extension
if is_exe(exe_file):
return exe_file
# versuche Programmnamen mit "weicher" Erweiterungssuche zu suchen
if len(os.path.splitext(fname)[1]) == 0:
for path in paths:
file_exts = list_file_exts(path, fname, not case_sensitive)
for file_ext in file_exts:
filename = "".join(file_ext)
exe_file = os.path.join(path, filename)
if is_exe(exe_file):
return exe_file
return None
Die Verwendung sieht folgendermaßen aus:
>>> which.which("meld")
'C:\\Program Files (x86)\\Meld\\meld\\meld.exe'
Die akzeptierte Lösung hat in meinem Fall nicht funktioniert, da Dateien wie meld.1
, meld.ico
, meld.doap
usw. auch im Verzeichnis vorhanden waren, von denen eine zurückgegeben wurde (vermutlich da lektografisch zuerst), weil der ausführbare Test in der akzeptierten Antwort unvollständig war und falsche Positiven lieferte.
4 Stimmen
Was ist falsch daran, nach der PATH-Umgebungsvariablen zu suchen? Was denkst du, was das UNIX-Befehl 'which' tut?
1 Stimmen
Ist das which.py Skript aus der Standardbibliothek ein einfacher Weg?
0 Stimmen
@J.F. - Das which.py-Skript, das mit Python enthalten ist, hängt von 'ls' ab, und einige der anderen Kommentare deuten darauf hin, dass Piotr nach einer plattformübergreifenden Antwort gesucht hat.
0 Stimmen
@Jay: Vielen Dank für den Kommentar. Ich habe coreutils auf Windows installiert, daher ist mir nicht aufgefallen, dass which.py unix-spezifisch ist.
0 Stimmen
Es gibt auch
which
, das Drittanbietermodul: code.activestate.com/pypm/which0 Stimmen
Zusammenhängend: stackoverflow.com/questions/646955/… Die Frage betrifft Windows, aber der Code funktioniert auch auf Posix-Geräten.
0 Stimmen
Beachten Sie, dass os.access() in Python 3.5 oder später nur Windows ACLs berücksichtigt (siehe bugs.python.org/issue2528).
3 Stimmen
Vielleicht sollte stackoverflow.com/a/13936916/145400 die akzeptierte Antwort im Jahr 2020 sein? :)