subprocess
在找不到命令的情况下会抛出一个异常OSError
。
当找到命令并且subprocess
为您运行命令时,结果代码将从命令中返回。标准是代码0表示成功,任何失败都是一些非零错误代码(这种错误代码有所不同,请检查您正在运行的特定命令的文档)。
因此,如果捕获了OSError
,则可以处理不存在的命令,如果检查结果代码,则可以找出命令是否成功。
subprocess
的好处在于,您可以使其收集stdout
和stderr
中的所有文本,然后将其丢弃、返回、记录或显示。我经常使用一个包装器来丢弃来自命令的所有输出,除非命令失败,在这种情况下,来自stderr
的文本将被输出。
我同意您不应该要求用户复制可执行文件。程序应位于PATH
变量中列出的目录中;如果缺少程序,则应安装该程序,或者如果该程序安装在未列在PATH
中的目录中,则用户应更新PATH
以包括该目录。
请注意,您可以尝试使用各种硬编码的可执行文件路径多次运行subprocess
:
import os
import subprocess as sp
def _run_cmd(s_cmd, tup_args):
lst_cmd = [s_cmd]
lst_cmd.extend(tup_args)
result = sp.call(lst_cmd)
return result
def run_lumberjack(*tup_args):
try:
return _run_cmd("/usr/local/bin/lumberjack", tup_args)
except OSError:
pass
try:
return _run_cmd("/opt/forest/bin/lumberjack", tup_args)
except OSError:
pass
try:
home = os.getenv("HOME", ".")
s_cmd = home + "/bin/lumberjack"
return _run_cmd(s_cmd, tup_args)
except OSError:
pass
raise OSError("could not find lumberjack in the standard places")
run_lumberjack("-j")
编辑:经过一番思考,我决定彻底重写上述内容。只需传递一个位置列表,并使用循环尝试备用位置,这样会更加简洁。但是如果不需要构建用户主目录的字符串,我就不想这样做,所以我只是让在备选列表中放入可调用对象合法。如果您对此有任何疑问,请随时提出。
import os
import subprocess as sp
def try_alternatives(cmd, locations, args):
"""
Try to run a command that might be in any one of multiple locations.
Takes a single string argument for the command to run, a sequence
of locations, and a sequence of arguments to the command. Tries
to run the command in each location, in order, until the command
is found (does not raise OSError on the attempt).
"""
lst_cmd = [None]
lst_cmd.extend(args)
for path in locations:
if callable(path):
path = path()
lst_cmd[0] = os.path.join(path, cmd)
try:
return sp.call(lst_cmd)
except OSError:
pass
raise OSError('command "{}" not found in locations list'.format(cmd))
def _home_bin():
home = os.getenv("HOME", ".")
return os.path.join(home, "bin")
def run_lumberjack(*args):
locations = [
"/usr/local/bin",
"/opt/forest/bin",
_home_bin,
]
return try_alternatives("lumberjack", locations, args)
run_lumberjack("-j")
check_call()
应该引发错误。 - inspectorG4dgetcall
和check_call
有点困惑。call
已经给你返回代码了——不是作为属性,而是函数的返回值。使用check_call
的原因是它会帮你检查返回代码,如果有问题就会引发CalledProcessError
异常。无论哪种方法都会因找不到程序而引发OSError
(或类似的错误,具体取决于你的Python版本)。 - abarnertOSError
而不是CalledProcessError
,即使使用call
而不仅仅是check_call
也是如此。请参见Theodros Zelleke的答案。 - abarnert