Проверить, существует ли исполняемый файл в Python? - PullRequest
267 голосов
/ 18 декабря 2008

В Python есть ли портативный и простой способ проверить, существует ли исполняемая программа?

Под простым я подразумеваю что-то вроде which команды, которая была бы просто идеальной. Я не хочу искать PATH вручную или что-то, связанное с попыткой выполнить его с помощью Popen & al, и посмотреть, не получится ли это (это то, что я делаю сейчас, но представьте, что это launchmissiles)

Ответы [ 21 ]

312 голосов
/ 18 декабря 2008

Самый простой способ, которым я могу придумать:

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

Редактировать : Обновлен пример кода, чтобы включить логику для обработки случая, когда предоставленный аргумент уже является полным путем к исполняемому файлу, т.е. Это имитирует поведение UNIX-команды 'which'.

Редактировать : Обновлено для использования os.path.isfile () вместо os.path.exists () для комментариев.

Редактировать : path.strip('"') кажется неправильной вещью здесь. Ни Windows, ни POSIX, похоже, не поддерживают цитируемые элементы PATH.

291 голосов
/ 27 сентября 2012

Я знаю, что это древний вопрос, но вы можете использовать distutils.spawn.find_executable. Это было задокументировано с python 2.4 и существует с python 1.6.

import distutils.spawn
distutils.spawn.find_executable("notepad.exe")

Кроме того, Python 3.3 теперь предлагает shutil.which().

126 голосов
/ 18 декабря 2012

Python 3.3 теперь предлагает shutil.which () .

33 голосов
/ 07 марта 2015

Для Python 3.2 и более ранних версий:

my_command = 'ls'
any(os.access(os.path.join(path, my_command), os.X_OK) for path in os.environ["PATH"].split(os.pathsep))

Это однострочник Ответ Джея , также здесь как лямбда-функция:

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

Или, наконец, с отступом в виде функции:

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

Для Python 3.3 и более поздних версий:

import shutil

command = 'ls'
shutil.which(command) is not None

Как однострочник Ян-Филипп Герке Ответ :

cmd_exists = lambda x: shutil.which(x) is not None

По умолчанию:

def cmd_exists(cmd):
    return shutil.which(cmd) is not None
17 голосов
/ 19 декабря 2008

Просто не забудьте указать расширение файла в Windows. В противном случае вы должны написать очень сложную is_exe для окон, используя переменную окружения PATHEXT. Вы можете просто использовать FindPath .

OTOH, почему вы вообще пытаетесь найти исполняемый файл? Операционная система сделает это за вас как часть вызова popen и вызовет исключение, если исполняемый файл не найден. Все, что вам нужно сделать, это перехватить правильное исключение для данной ОС. Обратите внимание, что в Windows subprocess.Popen(exe, shell=True) завершится с ошибкой, если exe не найден.


Включение PATHEXT в вышеприведенную реализацию which (в ответе Джея):

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
16 голосов
/ 17 июня 2012

Для * nix-платформ (Linux и OS X)

Мне кажется, это работает:

Отредактировано для работы в Linux, благодаря Mestreion

def cmd_exists(cmd):
    return subprocess.call("type " + cmd, shell=True, 
        stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0

Здесь мы используем встроенную команду type и проверяем код выхода. Если такой команды нет, type завершится с 1 (или, в любом случае, с ненулевым кодом состояния).

Немного о stdout и stderr - просто заставить замолчать вывод команды type, так как нас интересует только код состояния выхода.

Пример использования:

>>> cmd_exists("jsmin")
True
>>> cmd_exists("cssmin")
False
>>> cmd_exists("ls")
True
>>> cmd_exists("dir")
False
>>> cmd_exists("node")
True
>>> cmd_exists("steam")
False
7 голосов
/ 18 декабря 2008

См. os.path модуль для некоторых полезных функций в путевых именах. Чтобы проверить, является ли существующий файл исполняемым, используйте os.access (путь, режим) с режимом os.X_OK.

os.X_OK

Значение, которое необходимо включить в параметр режима доступа () для определения возможности выполнения пути.

РЕДАКТИРОВАТЬ: В предлагаемых which() реализациях отсутствует одна подсказка - использование os.path.join() для построения полных имен файлов.

5 голосов
/ 31 августа 2013

Я знаю, что я здесь немного некромант, но я наткнулся на этот вопрос, и принятое решение не сработало для меня во всех случаях. Думаю, что в любом случае было бы полезно отправить его. В частности, обнаружение «исполняемого» режима и требование предоставления расширения файла. Кроме того, оба * python3.3 shutil.which (использует PATHEXT) и distutils.spawn.find_executable python2.4 + (просто пытается добавить '.exe') работают только в подмножестве случаев.

Итак, я написал «супер» версию (основываясь на принятом ответе и предложении PATHEXT от Сураджа). Эта версия which выполняет задачу немного более тщательно, и сначала пробует серию «широкофазных» техник, ориентированных на ширину, и в конечном итоге пытается выполнить более детальный поиск в пространстве 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

Использование выглядит следующим образом:

>>> which.which("meld")
'C:\\Program Files (x86)\\Meld\\meld\\meld.exe'

Принятое решение в этом случае не сработало, так как в каталоге были также файлы, такие как meld.1, meld.ico, meld.doap и т. Д., Один из которых был возвращен (предположительно, так как сначала лексикографически) потому что выполняемый тест в принятом ответе был неполным и дал ложные срабатывания.

5 голосов
/ 26 июля 2012

На основании того, что проще просить прощения, чем разрешения Я бы просто попытался использовать его и поймать ошибку (в данном случае OSError - я проверил, что файл не существует, а файл не является исполняемым) и они оба дают OSError).

Помогает, если исполняемый файл имеет что-то вроде --version флага, который является быстрым запретом.

import subprocess
myexec = "python2.8"
try:
    subprocess.call([myexec, '--version']
except OSError:
    print "%s not found on path" % myexec

Это не общее решение, но оно будет самым простым способом для многих случаев использования - тех, где код должен искать один хорошо известный исполняемый файл.

4 голосов
/ 13 ноября 2014

Лучшим примером должен быть встроенный модуль Python shutil.which () в Python 3. Ссылка: https://hg.python.org/cpython/file/default/Lib/shutil.py

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...