En Python, existe-t-il un moyen simple et portable de vérifier si un programme exécutable existe?
Par simple j'entends quelque chose comme la commande which
qui serait tout simplement parfaite. Je ne veux pas rechercher manuellement PATH ou quelque chose impliquant d'essayer de l'exécuter avec Popen
& al et voir si cela échoue (c'est ce que je fais maintenant, mais imaginez que c'est launchmissiles
)
Le moyen le plus simple auquel je puisse penser:
def which(program):
import os
def is_exe(fpath):
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
fpath, fname = os.path.split(program)
if fpath:
if is_exe(program):
return program
else:
for path in os.environ["PATH"].split(os.pathsep):
exe_file = os.path.join(path, program)
if is_exe(exe_file):
return exe_file
return None
Edit: Exemple de code mis à jour pour inclure une logique de traitement du cas où l’argument fourni est déjà un chemin complet vers l’exécutable, c’est-à-dire "quel/bin/ls". Cela imite le comportement de la commande UNIX 'which'.
Edit: Mise à jour pour utiliser os.path.isfile () au lieu de os.path.exists () par commentaire.
Edit: path.strip('"')
semble être la mauvaise chose à faire ici. Ni Windows ni POSIX ne semblent encourager les éléments PATH cités.
Je sais que cette question est ancienne, mais vous pouvez utiliser distutils.spawn.find_executable
. Cela a été documenté depuis python 2.4 et existe depuis python 1.6.
import distutils.spawn
distutils.spawn.find_executable("notepad.exe")
De plus, Python 3.3 offre maintenant shutil.which()
.
Python 3.3 offre maintenant shutil.which () .
my_command = 'ls'
any(os.access(os.path.join(path, my_command), os.X_OK) for path in os.environ["PATH"].split(os.pathsep))
Ceci est une ligne de la réponse de Jay , également ici en tant que fonction lambda:
cmd_exists = lambda x: any(os.access(os.path.join(path, x), os.X_OK) for path in os.environ["PATH"].split(os.pathsep))
cmd_exists('ls')
Ou enfin, en retrait en tant que fonction:
def cmd_exists(cmd):
return any(
os.access(os.path.join(path, cmd), os.X_OK)
for path in os.environ["PATH"].split(os.pathsep)
)
import shutil
command = 'ls'
shutil.which(command) is not None
En une ligne de Jan-Philip Gehrcke Answer :
cmd_exists = lambda x: shutil.which(x) is not None
En tant que def:
def cmd_exists(cmd):
return shutil.which(cmd) is not None
N'oubliez pas de spécifier l'extension de fichier sous Windows. Sinon, vous devez écrire un is_exe
beaucoup plus compliqué pour les fenêtres utilisant la variable d’environnement PATHEXT
. Vous voudrez peut-être simplement utiliser FindPath .
OTOH, pourquoi vous même dérangez-vous de rechercher l'exécutable? Le système d'exploitation le fera pour vous dans le cadre de popen
call & lèvera une exception si l'exécutable n'est pas trouvé. Tout ce que vous avez à faire est d’attraper l’exception correcte pour un système d’exploitation donné. Notez que sous Windows, subprocess.Popen(exe, Shell=True)
échouera silencieusement si exe
n’est pas trouvé.
Intégration de PATHEXT
dans la mise en oeuvre de which
ci-dessus (dans la réponse de Jay):
def which(program):
def is_exe(fpath):
return os.path.exists(fpath) and os.access(fpath, os.X_OK) and os.path.isfile(fpath)
def ext_candidates(fpath):
yield fpath
for ext in os.environ.get("PATHEXT", "").split(os.pathsep):
yield fpath + ext
fpath, fname = os.path.split(program)
if fpath:
if is_exe(program):
return program
else:
for path in os.environ["PATH"].split(os.pathsep):
exe_file = os.path.join(path, program)
for candidate in ext_candidates(exe_file):
if is_exe(candidate):
return candidate
return None
Cela semble fonctionner pour moi:
Modifié pour fonctionner sous Linux, grâce à Mestreion
def cmd_exists(cmd):
return subprocess.call("type " + cmd, Shell=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0
Ce que nous faisons ici est d'utiliser la commande intégrée type
et de vérifier le code de sortie. S'il n'y a aucune commande de ce type, type
quittera avec 1 (ou un code d'état différent de zéro de toute façon).
Le bit à propos de stdout et stderr est juste pour faire taire la sortie de la commande type
, car nous ne nous intéressons qu'au code de statut de sortie.
Exemple d'utilisation:
>>> cmd_exists("jsmin")
True
>>> cmd_exists("cssmin")
False
>>> cmd_exists("ls")
True
>>> cmd_exists("dir")
False
>>> cmd_exists("node")
True
>>> cmd_exists("Steam")
False
Voir le module os.path pour quelques fonctions utiles sur les noms de chemins. Pour vérifier si un fichier existant est exécutable, utilisez os.access (chemin, mode) , avec le mode os.X_OK.
os.X_OK
Valeur à inclure dans le paramètre de mode de access () pour déterminer si le chemin peut être exécuté.
EDIT: Les implémentations suggérées de which()
manquent d'un indice - utilisez os.path.join()
pour créer des noms de fichiers complets.
Sur la base qu'il est plus facile de demander pardon que permission Je voudrais simplement essayer de l'utiliser et de détecter l'erreur (OSError dans ce cas - j'ai vérifié que le fichier n'existe pas et que le fichier n'est pas exécutable et les deux donnent OSError).
Cela est utile si l'exécutable a quelque chose comme un drapeau --version
qui est un non-op rapide.
import subprocess
myexec = "python2.8"
try:
subprocess.call([myexec, '--version']
except OSError:
print "%s not found on path" % myexec
Ce n'est pas une solution générale, mais le moyen le plus simple pour de nombreux cas d'utilisation - ceux dans lesquels le code doit rechercher un seul exécutable bien connu.
Je sais que je suis un peu un nécromancien ici, mais je suis tombé sur cette question et la solution acceptée ne me convenait pas dans tous les cas. Je pensais qu'il serait peut-être utile de soumettre de toute façon. En particulier, la détection du mode "exécutable" et la nécessité de fournir l'extension de fichier. De plus, les shutil.which
de python3.3 (utilise PATHEXT
) et les distutils.spawn.find_executable
de python2.4 + (ne tente que d'ajouter '.exe'
) ne fonctionnent que dans un sous-ensemble de cas.
J'ai donc écrit une version "super" (basée sur la réponse acceptée et sur la suggestion PATHEXT
de Suraj). Cette version de which
effectue cette tâche de manière un peu plus approfondie, et commence par une série de techniques "largephase" en largeur, puis par des recherches plus détaillées sur l'espace PATH
:
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
L'utilisation ressemble à ceci:
>>> which.which("meld")
'C:\\Program Files (x86)\\Meld\\meld\\meld.exe'
La solution acceptée ne fonctionnait pas pour moi dans ce cas, car il y avait des fichiers tels que meld.1
, meld.ico
, meld.doap
, etc. également dans le répertoire, dont un a été retourné à la place (vraisemblablement depuis lexicographiquement d’abord) car le test de l’exécutable dans la réponse acceptée était incomplet et donnait des faux positifs.
Le meilleur exemple devrait être le python module bulit-in shutil.which () dans Python 3. Le lien est https://hg.python.org/cpython /file/default/Lib/shutil.py
J'ai trouvé quelque chose dans StackOverflow qui a résolu le problème pour moi. Cela fonctionne à condition que l'exécutable ait une option (comme --help ou --version) qui génère quelque chose et renvoie un état de sortie égal à zéro. Voir Supprimer la sortie dans Python appels aux exécutables - le "résultat" à la fin de l'extrait de code dans cette réponse sera zéro si l'exécutable est dans le chemin, sinon c'est le plus susceptible d'être 1.
Cela semble assez simple et fonctionne à la fois en python 2 et 3
try: subprocess.check_output('which executable',Shell=True)
except: sys.exit('ERROR: executable not found')
Vous pouvez essayer la bibliothèque externe appelée "sh" ( http://amoffat.github.io/sh/ ).
import sh
print sh.which('ls') # prints '/bin/ls' depending on your setup
print sh.which('xxx') # prints None
Une question importante est " Pourquoi avez-vous besoin de tester si un exécutable existe?" Peut-être que vous ne le faites pas? ;-)
Récemment, j'avais besoin de cette fonctionnalité pour lancer l'afficheur pour un fichier PNG. Je voulais parcourir certains lecteurs prédéfinis et exécuter le premier qui existe. Heureusement, j'ai rencontré os.startfile
. C'est beaucoup mieux! Simple, portable et utilise le visualiseur default sur le système:
>>> os.startfile('yourfile.png')
Mise à jour: J'avais tort de dire que os.startfile
était portable ... Il ne s'agit que de Windows. Sur Mac, vous devez exécuter la commande open
. Et xdg_open
sous Unix. Il y a un problème Python lors de l'ajout du support Mac et Unix pour os.startfile
.
Ajout du support Windows
def which(program):
path_ext = [""];
ext_list = None
if sys.platform == "win32":
ext_list = [ext.lower() for ext in os.environ["PATHEXT"].split(";")]
def is_exe(fpath):
exe = os.path.isfile(fpath) and os.access(fpath, os.X_OK)
# search for executable under windows
if not exe:
if ext_list:
for ext in ext_list:
exe_path = "%s%s" % (fpath,ext)
if os.path.isfile(exe_path) and os.access(exe_path, os.X_OK):
path_ext[0] = ext
return True
return False
return exe
fpath, fname = os.path.split(program)
if fpath:
if is_exe(program):
return "%s%s" % (program, path_ext[0])
else:
for path in os.environ["PATH"].split(os.pathsep):
path = path.strip('"')
exe_file = os.path.join(path, program)
if is_exe(exe_file):
return "%s%s" % (exe_file, path_ext[0])
return None
Aucun des exemples précédents ne fonctionne sur toutes les plateformes. Habituellement, ils ne fonctionnent pas sous Windows car vous pouvez exécuter sans l'extension de fichier et que vous pouvez enregistrer une nouvelle extension. Par exemple sous Windows, si python est bien installé, il suffit d'exécuter 'file.py' et cela fonctionnera.
La seule solution valide et portable que j'avais était d'exécuter la commande et de voir le code d'erreur. Tout exécutable décent devrait avoir un ensemble de paramètres d’appel qui ne feront rien.
vous pouvez savoir si un fichier existe avec le module os. un exécutable en particulier semble tout à fait inutilisable étant donné que beaucoup de choses sont exécutables sur nix et non sur windows et inversement.
Il semblerait que le choix évident soit "qui", en analysant les résultats via popen, mais vous pouvez le simuler autrement en utilisant la classe os. En pseudopython, cela ressemblerait à ceci:
for each element r in path:
for each file f in directory p:
if f is executable:
return True
Donc, fondamentalement, vous voulez rechercher un fichier dans un système de fichiers monté (pas nécessairement uniquement dans les répertoires PATH) et vérifier s'il est exécutable. Cela se traduit par le plan suivant:
Je dirais que le faire de manière portable nécessitera beaucoup de temps et de puissance de calcul. Est-ce vraiment ce dont vous avez besoin?
Il existe un script which.py dans une distribution standard Python (par exemple sous Windows '\PythonXX\Tools\Scripts\which.py'
).
EDIT: which.py
dépend de ls
et n'est donc pas multiplateforme.