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


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

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

22 247

22 ответа:

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

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: обновленный пример кода для включения логики для обработки случая, когда предоставленный аргумент уже является полным путем к исполняемому файлу, т. е. "which /bin/ls". Это имитирует поведение команды UNIX 'which'.

Edit: обновлено для использования ОС.путь.isfile() вместо ОС.путь.существует() в комментариях.

Edit:path.strip('"') Кажется, что это неправильно. Ни Windows и POSIX, похоже, поощряют элементы пути в кавычках.

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

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

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

в Python 3.3 теперь предлагает shutil.который ().

для 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))

это один-лайнер Джей!--10-->, также здесь, как лямбда-функции:

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:

def cmd_exists(cmd):
    return shutil.which(cmd) is not None

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

OTOH, почему вы даже беспокоитесь о поиске исполняемого файла? Операционная система сделает это за вас в рамках popen звонок и вызывает исключение, если исполняемый файл не найден. Все, что вам нужно сделать, это поймать правильное исключение для данной ОС. Отмечать что на окнах,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

для платформ * 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

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

ОС.Прав

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

EDIT: предложил which() реализации не хватает одного ключа-с помощью os.path.join() построить полное имя файла.

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

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

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

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

я знаю, что я здесь немного некромант, но я наткнулся на этот вопрос, и принятое решение не сработало для меня для всех случаев, которые могли бы быть полезны для представления в любом случае. В частности, обнаружение "исполняемого" режима и требование предоставления расширения файла. Кроме того, оба python3.3-х shutil.which (использует PATHEXT) и python2. 4+ ' s distutils.spawn.find_executable (просто старается добавлять '.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 и т. д. Также в каталог, один из которых был возвращен вместо этого (предположительно, с лексикографически первого), потому что исполняемый тест в принятом ответе был неполным и давал ложные срабатывания.

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

Я нашел что-то в StackOverflow, что решило проблему для меня. Это работает при условии, что исполняемый файл имеет опцию (например, --help или --version), которая выводит что-то и возвращает статус выхода ноль. Смотрите подавление вывода в Python вызовов исполняемых файлов - "результат" в конце фрагмента кода в этом ответе будет ноль, если исполняемый файл находится в пути, то это, скорее всего, будет 1.

это кажется достаточно простым и работает как в python 2 и 3

try: subprocess.check_output('which executable',shell=True)
except: sys.exit('ERROR: executable not found')

если у вас bash и sh (subprocess.Popen( ... ).communicate() ),
используйте bash builtin type:

type -p ls  =>  /bin/ls
type -p nonesuch  =>  ""

важный вопрос-это "почему вам нужно проверить, существует ли исполняемый файл?- А может, и нет? ; -)

недавно мне понадобилась эта функция для запуска viewer для файла PNG. Я хотел перебрать некоторые предопределенные зрители и запустить первый, который существует. К счастью, я наткнулся os.startfile. Так гораздо лучше! Простой, портативный и использует по умолчанию просмотрщик в системе:

>>> os.startfile('yourfile.png')

обновление: Я ошибся насчет os.startfile быть портативным... Это только окна. На Mac, вы должны выполнить . И xdg_open на Unix. Там есть проблема в Python при добавлении поддержки Mac и Unix для os.startfile.

вы можете попробовать внешний lib называется " 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

добавлена поддержка 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

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

казалось бы очевидно выбор-это "что", анализ результатов через popen, но вы можете имитировать его иначе, используя класс os. В псевдопитоне это будет выглядеть так:

for each element r in path:
    for each file f in directory p:
        if f is executable:
           return True

поэтому в основном вы хотите найти файл в смонтированной файловой системе (не обязательно только в каталогах пути) и проверить, является ли он исполняемым. Это приводит к следующему плану:

  • перечислить все файлы в локально смонтированных файловых системах
  • результаты матча с именем pattern
  • для каждого найденного файла проверьте, является ли он исполняемым

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

есть which.py скрипт в стандартном дистрибутиве Python (например, на Windows '\PythonXX\Tools\Scripts\which.py').

EDIT:which.py зависит от ls поэтому это не кросс-платформенный.

ни один из предыдущих примеров не работает на всех платформах. Обычно они не работают на Windows, потому что вы можете выполнить без расширения файла и что вы можете зарегистрировать новое расширения. Например, в Windows, если python хорошо установлен, достаточно выполнить 'file.py-и это сработает.

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

использование библиотеки Python fabric:

from fabric.api import *

def test_cli_exists():
    """
    Make sure executable exists on the system path.
    """
    with settings(warn_only=True):
        which = local('which command', capture=True)

    if not which:
        print "command does not exist"

    assert which