Ich weiß, dass ich hier ein bisschen ein Nekromant bin, aber ich bin über diese Frage gestolpert und die akzeptierte Lösung hat nicht in allen Fällen für mich funktioniert. Ich dachte, es könnte nützlich sein, sie trotzdem einzureichen. Insbesondere die Erkennung des "ausführbaren" Modus und die Anforderung, die Dateierweiterung bereitzustellen. Darüber hinaus funktionieren sowohl Python3.3 shutil.which
(verwendet PATHEXT
) als auch Python2.4 + distutils.spawn.find_executable
(versucht nur das Hinzufügen '.exe'
) nur in einer Teilmenge von Fällen.
Also schrieb ich eine "Super" -Version (basierend auf der akzeptierten Antwort und dem PATHEXT
Vorschlag von Suraj). Diese Version von which
erledigt die Aufgabe etwas gründlicher und probiert zuerst eine Reihe von "Breitphasen" -Breiten-First-Techniken aus und versucht schließlich feinkörnigere Suchen über den 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):
""" Simulates unix `which` command. Returns absolute path if program found """
def is_exe(fpath):
""" Return true if fpath is a file we have access to that is executable """
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):
""" Return list of (filename, extension) tuples which match the search_filename"""
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)
# is a path: try direct program path
if fpath:
if is_exe(program):
return program
elif "win" in sys.platform:
# isnt a path: try fname in current directory on 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)
# try append program path per directory
for path in paths:
exe_file = os.path.join(path, program)
if is_exe(exe_file):
return exe_file
# try with known executable extensions per program path per directory
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
# try search program name with "soft" extension search
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 nicht für mich Arbeit in diesem Fall, da es gab Dateien wie meld.1
, meld.ico
, meld.doap
usw. auch in dem Verzeichnis, von denen stattdessen zurückgegeben wurden (vermutlich lexikographisch ersten seit) , weil der ausführbare Test in der akzeptierten Antwort war unvollständig und geben Fehlalarm.