在Python中,是否有一种可移植且简单的方法来测试可执行程序是否存在?
简单来说,我的意思是像which
命令这样的东西,将是完美的。我不想手动搜索PATH或涉及尝试使用Popen
&al 执行它的操作,以查看它是否失败(这就是我现在正在做的,但是可以想象是launchmissiles
)
which
第三方模块:code.activestate.com/pypm/which
在Python中,是否有一种可移植且简单的方法来测试可执行程序是否存在?
简单来说,我的意思是像which
命令这样的东西,将是完美的。我不想手动搜索PATH或涉及尝试使用Popen
&al 执行它的操作,以查看它是否失败(这就是我现在正在做的,但是可以想象是launchmissiles
)
which
第三方模块:code.activestate.com/pypm/which
Answers:
我想到的最简单的方法:
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项。
PATHEXT
env var 的值,因为它与vs command
一样有效command.com
script
script.bat
我知道这是一个古老的问题,但是您可以使用distutils.spawn.find_executable
。自python 2.4起已被记录下来,自python 1.6起就已存在。
import distutils.spawn
distutils.spawn.find_executable("notepad.exe")
此外,Python 3.3现在提供了shutil.which()
。
win32
,该distutils.spawn.find_executable
实现仅查找.exe
而不使用扩展列表搜索in中的set %PATHEXT%
。那不是很好,但是它可能适用于某人需要的所有情况。
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' object has no attribute 'spawn'
,虽然很奇怪,它可以在具有相同版本Python的同一台机器上运行,但是从virtualenv安装。
import distutils.spawn
或遵循from distutils import spawn
语法,而不仅仅是import distutils
。否则,它可能无法访问,AttributeError
即使有,您也将获得以上内容。
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's Answer的一线书,在这里也作为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
cmd_exists = lambda x: shutil.which(x) is not None
作为def:
def cmd_exists(cmd):
return shutil.which(cmd) is not None
x
在应使用的变量处使用变量cmd
os.path.join(path, cmd)
为文件,不是吗?毕竟,目录也可以设置可执行位...
mkdir -p -- "$HOME"/bin/dummy && PATH="$PATH":"$HOME"/bin && python -c 'import os; print any(os.access(os.path.join(path, "dummy"), os.X_OK) for path in os.environ["PATH"].split(os.pathsep))' && rmdir -- "$HOME"/bin/dummy
and os.path.isfile(...)
在适当的位置添加简单的内容即可解决此问题
只要记住在Windows上指定文件扩展名即可。否则,您必须is_exe
使用PATHEXT
环境变量为Windows 编写非常复杂的代码。您可能只想使用FindPath。
OTOH,为什么还要打扰搜索可执行文件?操作系统将在popen
调用过程中为您执行此操作,并且如果找不到可执行文件,则会引发异常。您需要做的就是捕获给定OS的正确异常。请注意,在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
yield
in的巧妙用法ext_candidates
,使我对关键字的工作原理有了更好的了解
这似乎为我工作:
借助Mestreion,经过编辑可在Linux上工作
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
type
它是内置的Shell,而不是可执行文件,因此subprocess.call()
在这里失败。
OSError: [Errno 2] No such file or directory
。也许在Mac中type
是实际的命令
shell=True
和替换["type", cmd]
为"type " + cmd
有关路径名的一些有用功能,请参见os.path模块。要检查现有文件是否可执行,请使用os.access(path,mode)和os.X_OK模式。
操作系统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
这不是一个通用的解决方案,但是对于许多用例来说,这将是最简单的方法-在那些用例中,代码需要寻找一个众所周知的可执行文件。
--version
名为launchmissiles
!的程序也太危险了。
launchmissies
存在,除非您想发射导弹?更好地执行它并根据退出状态/异常采取行动
git
,您可能不想盲目运行。
我知道我在这里有点死灵法师,但是我偶然发现了这个问题,并且公认的解决方案并不能在所有情况下对我都有效。特别是,“可执行”模式检测以及提供文件扩展名的要求。此外,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
,等也在目录中,其中一个被退回,而不是(大概是因为字典序第一),因为在接受答案的可执行测试是不完整的,并给予误报。
最好的示例应该是Python 3中的python bulit-in模块shutil.which()。链接为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')
command -v executable
或type executable
通用。在某些情况下,在Mac上无法返回预期结果。
您可以尝试名为“ 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
您可以判断os模块是否存在文件。考虑到很多事情是在Windows上无法运行的nix上执行的,尤其是一个可执行文件似乎非常不可移植,反之亦然。
似乎很明显的选择是“哪个”,它通过popen解析结果,但是您可以使用os类来模拟它。在伪python中,它看起来像这样:
for each element r in path:
for each file f in directory p:
if f is executable:
return True
which
命令。有一个UnxUtils版本,但是您必须知道/指定扩展名,否则将找不到该程序。
使用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
which(1)
所有系统上都不存在该程序。