在Python中,是否有一种可移植且简单的方法来测试可执行程序是否存在?
我说的简单是指像which命令这样完美的命令。我不想手动搜索PATH或涉及尝试与Popen & al执行它,看看它是否失败(这就是我现在做的,但想象它是launchmissiles)
在Python中,是否有一种可移植且简单的方法来测试可执行程序是否存在?
我说的简单是指像which命令这样完美的命令。我不想手动搜索PATH或涉及尝试与Popen & al执行它,看看它是否失败(这就是我现在做的,但想象它是launchmissiles)
当前回答
我知道我在这里有点死灵,但我偶然发现了这个问题,公认的解决方案并不是对所有情况都适用我认为无论如何提交都可能有用。特别是“可执行”模式的检测,以及提供文件扩展名的要求。此外,python3.3的shutil。(使用pathex)和python2.4+的distutils.spawn。Find_executable(只是尝试添加'.exe')只在一个子集的情况下工作。
所以我写了一个“超级”版本(基于公认的答案,以及Suraj的PATHEXT建议)。这个版本更彻底地完成了任务,并首先尝试了一系列“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这样的文件。1, meld.ico, meld。Doap等也在目录中,其中一个被返回(可能是在字典顺序上先返回的),因为接受的答案中的可执行测试是不完整的,并给出了错误的阳性结果。
其他回答
因此,基本上您希望在挂载的文件系统中找到一个文件(不一定只在PATH目录中)并检查它是否可执行。这可以转化为以下计划:
枚举本地挂载的文件系统中的所有文件 用名称模式匹配结果 对于找到的每个文件,检查它是否可执行
我想说,以一种便携式的方式来做这件事将需要大量的计算能力和时间。这真的是你需要的吗?
您可以尝试名为“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
我知道我在这里有点死灵,但我偶然发现了这个问题,公认的解决方案并不是对所有情况都适用我认为无论如何提交都可能有用。特别是“可执行”模式的检测,以及提供文件扩展名的要求。此外,python3.3的shutil。(使用pathex)和python2.4+的distutils.spawn。Find_executable(只是尝试添加'.exe')只在一个子集的情况下工作。
所以我写了一个“超级”版本(基于公认的答案,以及Suraj的PATHEXT建议)。这个版本更彻底地完成了任务,并首先尝试了一系列“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这样的文件。1, meld.ico, meld。Doap等也在目录中,其中一个被返回(可能是在字典顺序上先返回的),因为接受的答案中的可执行测试是不完整的,并给出了错误的阳性结果。
*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将以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
一个重要的问题是“为什么需要测试可执行文件是否存在?”也许你不知道?: -)
最近我需要这个功能来启动PNG文件的查看器。我想迭代一些预定义的查看器,并运行第一个存在的查看器。幸运的是,我遇到了os.startfile。好多了!简单,可移植,并使用系统上的默认查看器:
>>> os.startfile('yourfile.png')
更新:关于操作系统我错了。开始文件是可移植的…这是Windows独有的。在Mac上,你必须运行open命令。Unix上的xdg_open。添加Mac和Unix对os.startfile的支持存在Python问题。