检查python脚本中是否存在程序

Check if a program exists from a python script

本问题已经有最佳答案,请猛点这里访问。

如何从python脚本检查程序是否存在?

假设您要检查wgetcurl是否可用。 我们假设他们应该走在路上。

看到多平台解决方案是最好的,但目前Linux已经足够了。

提示:

  • 运行命令并检查返回代码并不总是足够的,因为即使您尝试--version,某些工具也会返回非0结果。
  • 检查命令时,屏幕上看不到任何内容

此外,我会感谢一个更通用的解决方案,如is_tool(name)


shutil.which

让我推荐一个尚未讨论的选项:which的Python实现,特别是shutil.which。它是在Python 3.3中引入的,是跨平台的,支持Linux,Mac和Windows。它也可以在Python 2.x中通过whichcraft获得。您也可以直接从which中删除which的代码并将其插入到您的程序中。

1
2
3
4
5
6
7
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

已经提到的另一个选项是distutils.spawn.find_executable

find_executable的文档字符串如下:

Tries to find 'executable' in the directories listed in 'path'

因此,如果您注意,您会注意到该功能的名称有些误导。与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需要注意的事情。

1
2
3
4
5
6
def is_tool(name):
   """Check whether `name` is on PATH."""

    from distutils.spawn import find_executable

    return find_executable(name) is not None


最简单的方法是尝试使用所需的参数运行程序,并处理异常(如果它不存在):

1
2
3
4
5
6
7
8
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

这是Python中的常见模式:EAFP


您可以使用对所需二进制文件的子进程调用:

  • "哪个":* nix
  • "where":Win 2003及更高版本(Xp有一个插件)

获取可执行文件路径(假设它在环境路径中)。

1
2
3
4
5
6
7
8
9
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脚本,这是一个"哪个"跨平台实现:

http://nedbatchelder.com/code/utilities/wh_py.html


我会去:

1
2
3
4
import distutils.spawn

def is_tool(name):
  return distutils.spawn.find_executable(name) is not None


1
2
3
4
5
6
7
8
9
10
11
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


我可能会发现which wgetwhich curl并检查结果是否以您正在使用的程序的名称结尾。 unix的魔力:)

实际上,您需要做的就是检查which的返回码。所以...使用我们可靠的subprocess模块:

1
2
3
4
5
6
7
import subprocess

rc = subprocess.call(['which', 'wget'])
if rc == 0:
    print 'wget installed!'
else:
    print 'wget missing in path!'

请注意,我在带有cygwin的windows上测试了这个...如果你想弄清楚如何在纯python中实现which,我建议你在这里查看:http://pypi.python.org/pypi/pycoreutils(哦亲爱的 - 似乎他们没有提供which。友好推动的时间?)

更新:在Windows上,您可以使用where而不是which来获得类似的效果。


我将@ sorin的答案更改如下,原因是它会检查程序的名称而不传递程序的绝对路径

1
2
3
4
5
6
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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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(),则可以处理通常在代码中打印到标准输出的字符串,并仍然捕获异常和退出状态代码。

如果要在终端中禁止标准输出流,请不要打印从check_output返回的标准输出字符串:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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.returncodesubprocess.CalledProcessError.output引发,因此您可以使用它们执行任何操作。

如果要将可执行文件的标准输出打印到终端,请打印返回的字符串:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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输出流,如上面的print()语句所示),请使用sys.stdout.write(string)sys.stderr.write(string)而不是print():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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)