当前位置:  开发笔记 > 编程语言 > 正文

测试Python中是否存在可执行文件?

如何解决《测试Python中是否存在可执行文件?》经验,为你挑选了9个好方法。

在Python中,是否有一种可移植且简单的方法来测试可执行程序是否存在?

简单来说,我的意思是which命令,这将是完美的.我不想手动搜索PATH或者试图用Popen&al 执行它,并查看它是否失败(这就是我现在正在做的,但想象一下launchmissiles)



1> Jay..:

我能想到的最简单方法:

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

编辑:更新的代码示例包含用于处理案例的逻辑,其中提供的参数已经是可执行文件的完整路径,即"which/bin/ls".这模仿了UNIX'which'命令的行为.

编辑:更新为每个注释使用os.path.isfile()而不是os.path.exists().

编辑:这里path.strip('"')似乎做错了.Windows和POSIX似乎都不鼓励引用引用的PATH项目.


我建议将"os.path.exists"改为"os.path.isfile".否则在Unix中,这可能会错误地匹配具有+ x位集的目录.我还发现将它添加到函数顶部很有用:import sys; 如果sys.platform =="win32"而不是program.endswith(".exe"):program + =".exe".这种方式在Windows下您可以引用"calc"或"calc.exe",就像在cmd窗口中一样.

2> Nathan Binke..:

我知道这是一个古老的问题,但你可以使用distutils.spawn.find_executable.这是自python 2.4以来已经记录的,并且自python 1.6以来就已存在.

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

此外,Python 3.3现在提供shutil.which().


@JoshKupershmidt,确保`import distutils.spawn`,或者遵循`from distutils import spawn`语法,而不仅仅是`import distutils`.否则它可能无法访问,即使它在那里你也会得到上面的`AttributeError`.
在`win32`上,`distutils.spawn.find_executable`实现只查找`.exe`而不是使用扩展列表来搜索`%PATHEXT%`中的set.这不是很好,但它可能适用于所有需要的案例.
示例用法:`from distutils import spawn``php_path = spawn.find_executable("php")`
显然`distutils.spawn`是不可靠的:在OS X 10.10的Python 2.7.6的系统安装(/ usr/bin/python)中,我得到:`AttributeError:'module'对象没有属性'spawn' `,虽然奇怪的是它可以在同一台机器上使用相同版本的Python,但是来自virtualenv安装.

3> Jan-Philip G..:

Python 3.3现在提供shutil.which().



4> ThorSummoner..:

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

这是Jay的答案,也是一个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')

或者最后,缩进为一个函数:

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

作为Jan-Philip Gehrcke的单行答案:

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

作为def:

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



5> Suraj..:

只需记住在Windows上指定文件扩展名即可.否则,你必须is_exe使用PATHEXT环境变量为windows 写一个非常复杂的.您可能只想使用FindPath.

OTOH,你为什么还要费心去搜索可执行文件呢?操作系统将作为popen调用的一部分为您执行此操作,如果找不到可执行文件,则会引发异常.您需要做的就是捕获给定操作系统的正确异常.请注意,在Windows上,subprocess.Popen(exe, shell=True)如果exe找不到,将无提示失败.


结合PATHEXT上述实现which(在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



6> hasen..:

对于*nix平台(Linux和OS X)

这似乎对我有用:

由于Mestreion,我被编辑在Linux上工作

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

我们在这里做的是使用builtin命令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


注意:确保变量"cmd"包含有效数据.如果它来自外部来源,坏人可以给你"ls; rm -rf /".我认为in-python解决方案(没有子进程)要好得多.下一点:如果经常调用此方法,则子进程解决方案要慢得多,因为需要生成大量进程.
你确定*这个有效吗?这是一个非常好的方法,但`type`是一个内置的shell,而不是一个可执行文件,所以`subprocess.call()`在这里失败了.
经过*LOT*测试后,我发现了如何修复:添加`shell = True`并将`["type",cmd]`替换为`"type"+ cmd`

7> gimel..:

有关路径名的一些有用功能,请参阅os.path模块.要检查现有文件是否可执行,请使用os.access(path,mode)和os.X_OK模式.

os.X_OK

要包含在access()的mode参数中以确定是否可以执行路径的值.

编辑:建议的which()实现缺少一个线索 - os.path.join()用于构建完整的文件名.



8> Hamish Downe..:

在请求宽恕比允许更容易的基础上,我会尝试使用它并捕获错误(在这种情况下OSError - 我检查文件不存在,文件不可执行,它们都给出了OSError).

如果可执行文件具有类似于--version快速无操作的标志,则会有所帮助.

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

这不是一般解决方案,但对于许多用例来说是最简单的方法 - 代码需要查找一个众所周知的可执行文件.


甚至在名为`launchmissiles`的程序上调用`--version`也太危险了!

9> Preet Kukret..:

我知道我在这里是一个死灵法师,但我偶然发现了这个问题,并且所接受的解决方案对我来说并不适用于所有情况.无论如何,提交它可能是有用的.特别是"可执行"模式检测,以及提供文件扩展名的要求.此外,python3.3 shutil.which(使用PATHEXT)和python2.4 + distutils.spawn.find_executable(仅尝试添加'.exe')仅适用于案例的子集.

所以我写了一个"超级"版本(基于接受的答案,以及PATHEXTSuraj 的建议).这个版本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,等也在目录中,分别代替(大概是因为字典序第一),因为在接受答案的可执行测试是不完整的,并给予回到其中一个误报.

推荐阅读
可爱的天使keven_464
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有