web-dev-qa-db-fra.com

Tester si un exécutable existe en Python?

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)

273
Piotr Lesnicki

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.

314
Jay

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() .

296
Nathan Binkert

Python 3.3 offre maintenant shutil.which () .

135
Jan-Philip Gehrcke

Pour python 3.2 et versions antérieures:

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)
    )

Pour python 3.3 et versions ultérieures:

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
34
ThorSummoner

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
18
Suraj

Pour les plates-formes * nix (Linux et OS X)

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
16
hasen

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.

7
gimel

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.

5
Hamish Downer

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.

5
Preet Kukreti

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

4
liuyix

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.

2
Somesh

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')
2
jaap

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
1
jung rhew

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.

1
ash

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
1
wukong

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.

0
sorin

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.

0
Dustin Getz

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
0
Charlie Martin

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:

  • énumérer tous les fichiers dans des systèmes de fichiers montés localement
  • apparier les résultats avec le modèle de nom
  • pour chaque fichier trouvé, vérifiez s'il est exécutable

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?

0
zgoda

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.

0
jfs