如何检查 python 脚本中是否存在程序?
假设您要检查是否wget
可用curl
。我们将假设它们应该在路径中。
最好看到多平台解决方案,但目前,Linux 就足够了。
提示:
- 运行命令并检查返回代码并不总是足够的,因为即使您尝试,某些工具也会返回非 0 结果
--version
。 - 检查命令时,屏幕上不应显示任何内容
另外,我希望有一个更通用的解决方案,比如is_tool(name)
如何检查 python 脚本中是否存在程序?
假设您要检查是否wget
可用curl
。我们将假设它们应该在路径中。
最好看到多平台解决方案,但目前,Linux 就足够了。
提示:
--version
。另外,我希望有一个更通用的解决方案,比如is_tool(name)
让我推荐一个尚未讨论过的选项:一个 Python 实现which
,特别是shutil.which
. 它是在 Python 3.3 中引入的,并且是跨平台的,支持 Linux、Mac 和 Windows。它也可以通过whichcraft在 Python 2.x 中使用。您也可以which
直接从 whichcraft 中提取代码并将其插入到您的程序中。
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
的文档字符串如下:
尝试在“路径”中列出的目录中查找“可执行文件”
因此,如果您注意,您会注意到该函数的名称有些误导。与which
,不同的是,find_executable
它实际上并没有验证它executable
被标记为可执行文件,只是它在 PATH 上。因此,完全有可能(尽管不太可能)find_executable
表明程序可用,而实际上它不可用。
例如,假设您有一个/usr/bin/wget
未标记为可执行的文件。从 shell运行wget
将导致以下错误:bash: /usr/bin/wget: Permission denied。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 中,您必须改为 catch OsError
,因为尚不存在针对 OS 错误的更细粒度的异常类:
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
您可以使用子进程调用所需的二进制文件:
获取可执行路径(假设它在环境路径中)。
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 脚本,这是一个“哪个”跨平台实现:
import 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
我会去:
import distutils.spawn
def is_tool(name):
return distutils.spawn.find_executable(name) is not None
我可能会掏出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!'
请注意,我使用 cygwin 在 Windows 上对此进行了测试...如果您想弄清楚如何which
在纯 python 中实现,我建议您在这里查看:http: //pypi.python.org/pypi/pycoreutils(哦,亲爱的 - 似乎他们不提供which
。是时候友好推动了?)
更新:在 Windows 上,您可以使用where
代替来which
获得类似的效果。
我将@sorin的答案更改如下,原因是它会检查程序的名称而不传递程序的绝对路径
from 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
import 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
对@SvenMarnach 的代码稍作修改,解决了打印到标准输出流的问题。如果您使用该subprocess.check_output()
函数而不是subprocess.call()
then 您可以处理通常打印到代码中标准输出的字符串,并且仍然可以捕获异常和退出状态代码。
如果要抑制终端中的标准输出流,请不要打印从以下返回的标准输出字符串check_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)
非零退出状态代码和输出字符串在CalledProcessError
as 中提出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()
在字符串中添加一个额外的换行符。如果要消除这种情况(并将 std 错误写入 std err 流而不是 std out 流,如上面的 print() 语句所示),请使用sys.stdout.write(string)
andsys.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)