在Python中,是否有一种可移植且简单的方法来测试可执行程序是否存在?
简单来说,我的意思是which
命令,这将是完美的.我不想手动搜索PATH或者试图用Popen
&al 执行它,并查看它是否失败(这就是我现在正在做的,但想象一下launchmissiles
)
我能想到的最简单方法:
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项目.
我知道这是一个古老的问题,但你可以使用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.which().
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) )
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
只需记住在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
这似乎对我有用:
由于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
有关路径名的一些有用功能,请参阅os.path模块.要检查现有文件是否可执行,请使用os.access(path,mode)和os.X_OK模式.
os.X_OK
要包含在access()的mode参数中以确定是否可以执行路径的值.
编辑:建议的which()
实现缺少一个线索 - os.path.join()
用于构建完整的文件名.
在请求宽恕比允许更容易的基础上,我会尝试使用它并捕获错误(在这种情况下OSError - 我检查文件不存在,文件不可执行,它们都给出了OSError).
如果可执行文件具有类似于--version
快速无操作的标志,则会有所帮助.
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 + distutils.spawn.find_executable
(仅尝试添加'.exe'
)仅适用于案例的子集.
所以我写了一个"超级"版本(基于接受的答案,以及PATHEXT
Suraj 的建议).这个版本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
,等也在目录中,分别代替(大概是因为字典序第一),因为在接受答案的可执行测试是不完整的,并给予回到其中一个误报.