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

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

所谓简单,我指的是像which这样的命令,这将是完美的。我不想手动搜索PATH或尝试使用Popen &al,看看它是否失败(这就是我现在正在做的,但想象它是launchmissiles)

174266 次浏览

我能想到的最简单的方法:

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

编辑:更新的代码示例,包括处理情况的逻辑,其中提供的参数已经是可执行文件的完整路径,即。“这/bin/ls”。这模拟了UNIX 'which'命令的行为。

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

编辑: path.strip('"')在这里似乎是错误的。Windows和POSIX似乎都不鼓励引用PATH项。

有关路径名的一些有用函数,请参阅os.path模块。要检查一个现有的文件是否可执行,使用操作系统。访问(路径、模式),带有os。X_OK模式。

操作系统。X_OK

要包含在access()的模式参数中的值,以确定是否可以执行path。

建议的which()实现缺少一个线索——使用os.path.join()来构建完整的文件名。

因此,基本上您希望在挂载的文件系统中找到一个文件(不一定只在PATH目录中)并检查它是否可执行。这可以转化为以下计划:

  • 枚举本地挂载的文件系统中的所有文件
  • 用名称模式匹配结果
  • 对于找到的每个文件,检查它是否可执行

我想说,以一种便携式的方式来做这件事将需要大量的计算能力和时间。这真的是你需要的吗?

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

哦,你为什么还要费心搜索可执行文件呢?操作系统会作为popen调用的一部分为你做这件事。如果未找到可执行文件,将引发异常。您所需要做的就是为给定的操作系统捕获正确的异常。注意,在Windows上,如果没有找到exesubprocess.Popen(exe, shell=True)将会静默失败。


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

在标准的Python发行版中有一个which.py脚本(例如在Windows '\PythonXX\Tools\Scripts\which.py'上)。

which.py依赖于ls,因此它不是跨平台的。

一个重要的问题是“为什么你需要测试可执行文件是否存在?”也许你不知道?: -)

最近我需要这个功能来启动PNG文件的查看器。我想迭代一些预定义的查看器,并运行第一个存在的查看器。幸运的是,我遇到了os.startfile。好多了!简单,可移植,并在系统上使用默认的查看器:

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

我错误的认为os.startfile是可移植的…这是Windows独有的。在Mac上,你必须运行open命令。和Unix上的xdg_open。有一个关于为os.startfile添加Mac和Unix支持的Python的问题

*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

基于它是请求原谅比请求允许容易(而且,重要的是,该命令可以安全运行),我会尝试使用它并捕获错误(在这种情况下,我检查了文件不存在和文件不可执行,它们都给出了OSError)。

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

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

这不是一个通用的解决方案,但对于很多用例来说是最简单的方法——在这些用例中,代码需要寻找一个已知的可执行文件,该文件可以安全运行,或者至少可以安全运行给定的标志。

我知道这是一个古老的问题,但你可以使用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标准库中的shutil.which ()。 电池包括!< / p >

我知道我在这里有点死灵,但我偶然发现了这个问题,公认的解决方案并不是对所有情况都适用我认为无论如何提交都可能有用。特别是“可执行”模式的检测,以及提供文件扩展名的要求。此外,python3.3的shutil.which(使用PATHEXT)和python2.4+的distutils.spawn.find_executable(只是尝试添加'.exe')都只在子集情况下有效。

所以我写了一个“超级”版本(基于已接受的答案,以及Suraj的PATHEXT建议)。这个版本的which更彻底地完成了任务,并首先尝试了一系列“broadphase”宽度优先技术,最终尝试在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.1meld.icomeld.doap等文件,其中一个被返回(大概是因为字典顺序第一),因为接受的答案中的可执行测试是不完整的,并给出假阳性。

这看起来很简单,在python2和python3中都可以使用

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

您可以尝试名为“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

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

对于python 3.2及更早版本:

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

这是周杰伦的回答的一行代码,在这里也是lambda func:

cmd_exists = lambda x: any((os.access(os.path.join(path, x), os.X_OK) and os.path.isfile(os.path.join(path, x))) for path in os.environ["PATH"].split(os.pathsep))
cmd_exists('ls')

最后,缩进为函数:

def cmd_exists(cmd, path=None):
""" test if path contains an executable file with name
"""
if path is None:
path = os.environ["PATH"].split(os.pathsep)


for prefix in path:
filename = os.path.join(prefix, cmd)
executable = os.access(filename, os.X_OK)
is_not_directory = os.path.isfile(filename)
if executable and is_not_directory:
return True
return False

增加了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