如何在Python脚本中检查程序是否存在?
假设您想要检查wget
或curl
是否可用,并且它们应该在路径中。
最好看到一个多平台的解决方案,但现在只需要Linux。
提示:
- 仅运行命令并检查返回代码并不总是足够的,因为有些工具即使尝试使用
--version
也会返回非0结果。 - 在检查命令时屏幕上不应显示任何内容。
另外,我希望得到一个更通用的解决方案,例如is_tool(name)
。
如何在Python脚本中检查程序是否存在?
假设您想要检查wget
或curl
是否可用,并且它们应该在路径中。
最好看到一个多平台的解决方案,但现在只需要Linux。
提示:
--version
也会返回非0结果。另外,我希望得到一个更通用的解决方案,例如is_tool(name)
。
推荐一种尚未讨论过的选项:Python实现的which命令,具体来说是shutil.which
。它在Python 3.3中被引入,并且跨平台支持Linux、Mac和Windows。它也可以通过whichcraft获得Python 2.x版本。您还可以从whichcraft的这里直接提取which
代码并将其插入到您的程序中。
def is_tool(name):
"""Check whether `name` is on PATH and marked as executable."""
# from whichcraft import which
from shutil import which
return which(name) is not None
另一个已经提到的选择是distutils.spawn.find_executable
。
find_executable
的文档字符串如下:
尝试在“path”中列出的目录中查找“executable”
因此,如果您注意到,您会发现该函数的名称有些误导。与which
不同,find_executable
实际上并不验证 executable
是否已标记为可执行,只验证它是否在PATH中。因此,find_executable
指示可用程序时(虽然可能不太可能),实际上可能不存在该程序。
例如,假设您有一个未标记为可执行的文件/usr/bin/wget
。从shell中运行wget
将导致以下错误:bash:/usr/bin/wget:权限被拒绝。which('wget') is not None
将返回False,但find_executable('wget') is not None
将返回True。您可能可以使用任何一个函数,但这只是需要注意的find_executable
的一点。
def is_tool(name):
"""Check whether `name` is on PATH."""
from distutils.spawn import find_executable
return find_executable(name) is not None
最简单的方法是尝试使用所需参数运行程序,如果不存在,则处理异常:
try:
subprocess.call(["wget", "your", "parameters", "here"])
except FileNotFoundError:
# handle file not found error.
这是Python中的一种常见模式:EAFP
在Python 2中,由于操作系统错误的更细粒度的异常类还不存在,您必须捕获OsError
异常:
try:
subprocess.call(["wget", "your", "parameters", "here"])
except OSError as e:
if e.errno == errno.ENOENT:
# handle file not found error.
else:
# Something else went wrong while trying to run `wget`
raise
FileNotFoundError
。可以省略 if 语句。 - fabian789你可以使用子进程调用运行二进制文件,具体方法如下:
使用上述方法可以获取可执行文件路径(假设该文件已在环境路径中)。
import os
import platform
import subprocess
cmd = "where" if platform.system() == "Windows" else "which"
try:
subprocess.call([cmd, your_executable_to_check_here])
except:
print "No executable"
或者直接使用Ned Batchelder的wh.py脚本,这是一个跨平台的"which"实现:
我会使用which wget
或者which curl
的方式获取相应程序,然后检查结果是否以你正在使用的程序名结尾。Unix的魔力 :)
实际上,你只需要检查which
的返回代码即可。因此... 使用我们可靠的subprocess
模块:
import subprocess
rc = subprocess.call(['which', 'wget'])
if rc == 0:
print('wget installed!')
else:
print('wget missing in path!')
注意,我在Windows上使用cygwin进行了测试...如果你想找出如何在纯Python中实现which
,我建议你在这里查看:http://pypi.python.org/pypi/pycoreutils(哦,亲爱的 - 看来他们没有提供which
。时间来友善地推一下?)
更新:在Windows上,您可以使用where
代替which
以获得类似的效果。"which wget"
的程序,即文件名中带有空格吗? - Sven Marnachsubprocess.call(['which', 'wget'])
- nbubisimport subprocess
import os
def is_tool(name):
try:
devnull = open(os.devnull)
subprocess.Popen([name], stdout=devnull, stderr=devnull).communicate()
except OSError as e:
if e.errno == os.errno.ENOENT:
return False
return True
os.devnull
作为标准输出和标准错误流。请注意,在这种情况下,进程不会输出到控制台。 - Sven Marnachopen(os.devnull, "w")
- yadutafsubprocess.DEVNULL
而不是 open(os.devnull)
。 - Sixos.devnull
。否则,如果执行多次,将会有太多的句柄打开,最终将超过限制。 - Astitva Srivastava我的选择是:
import distutils.spawn
def is_tool(name):
return distutils.spawn.find_executable(name) is not None
distutils.spawn
在 Linux 和 Mac OS X 上运行良好。但在后者中,如果您制作了一个 app
,并双击执行,distutils.spawn
总是返回 None
。 - muammarfrom subprocess import Popen, PIPE
def check_program_exists(name):
p = Popen(['/usr/bin/which', name], stdout=PIPE, stderr=PIPE)
p.communicate()
return p.returncode == 0
which
不具备可移植性,不符合 POSIX 标准,并且在大多数平台上默认未安装。请使用 command -v
或 type
命令。 - sorinimport os
import subprocess
def is_tool(prog):
for dir in os.environ['PATH'].split(os.pathsep):
if os.path.exists(os.path.join(dir, prog)):
try:
subprocess.call([os.path.join(dir, prog)],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
except OSError, e:
return False
return True
return False
os.path.join()
和 os.pathsep
。 - Sven Marnachos.pathsep
- 在Windows上,PATH
是用分号分隔的。) - Sven Marnachnslookup
。 - muammar对@SvenMarnach代码的轻微修改,解决了打印到标准输出流的问题。如果您使用subprocess.check_output()
函数而不是subprocess.call()
,那么您可以在代码中处理通常打印到标准输出的字符串,并仍然捕获异常和退出状态码。
如果您想在终端中抑制标准输出流,请不要打印从check_output
返回的std out字符串:
import subprocess
import os
try:
stdout_string = subprocess.check_output(["wget", "--help"], stderr=subprocess.STDOUT)
# print(stdout_string)
except subprocess.CalledProcessError as cpe:
print(cpe.returncode)
print(cpe.output)
except OSError as e:
if e.errno == os.errno.ENOENT:
print(e)
else:
# Something else went wrong while trying to run `wget`
print(e)
非零退出状态码和输出字符串在 CalledProcessError
中作为 subprocess.CalledProcessError.returncode
和 subprocess.CalledProcessError.output
抛出,因此您可以根据需要进行处理。
如果您想将可执行文件的标准输出打印到终端,请打印返回的字符串:
import subprocess
import os
try:
stdout_string = subprocess.check_output(["wget", "--help"], stderr=subprocess.STDOUT)
print(stdout_string)
except subprocess.CalledProcessError as cpe:
print(cpe.returncode)
print(cpe.output)
except OSError as e:
if e.errno == os.errno.ENOENT:
print(e)
else:
# Something else went wrong while trying to run `wget`
print(e)
print()
函数会在字符串末尾添加一个额外的换行符。如果你想要消除这个换行符(并且像上面的print()
语句一样将标准错误写入标准错误流而不是标准输出流),可以使用sys.stdout.write(string)
和sys.stderr.write(string)
代替print()
:
import subprocess
import os
import sys
try:
stdout_string = subprocess.check_output(["bogus"], stderr=subprocess.STDOUT)
sys.stdout.write(stdout_string)
except subprocess.CalledProcessError as cpe:
sys.stderr.write(cpe.returncode)
sys.stderr.write(cpe.output)
except OSError as e:
if e.errno == os.errno.ENOENT:
sys.stderr.write(e.strerror)
else:
# Something else went wrong while trying to run `wget`
sys.stderr.write(e.strerror)
shutil.which()
的文档链接:https://docs.python.org/zh-cn/3/library/shutil.html#shutil.which - Nick Chammaswhich
不是一个好选择。你应该使用command -v
代替。另请参见如何从Bash脚本中检查程序是否存在?和如何在shell脚本中检查命令是否存在? - jwwshutil.which
,网址:https://github.com/python/cpython/blob/3e986de0d65e78901b55d4e500b1d05c847b6d5e/Lib/shutil.py#L1291 或 https://github.com/pydanny/whichcraft/blob/master/whichcraft.py#L20。它是纯 Python 编写的,不会调用which
二进制文件,所以那些答案中提出的参数都不相关。 - Sixreturn which(name) is not None
这一行吗? - Peter Schornfind_executable(name)
如果找不到任何东西,就会返回None
。因此,在None is not None
中将会是False
,因此return False
。当它不是None
时,条件变为True
并返回True
。 - Hossein