subprocess
OSError
当找不到命令时,将引发异常。
当找到该命令并subprocess
为您运行该命令时,将从该命令返回结果代码。标准是代码 0 表示成功,任何失败都是一些非零错误代码(会有所不同;请查看您正在运行的特定命令的文档)。
所以,如果你抓住OSError
了你可以处理不存在的命令,如果你检查结果代码,你可以找出命令是否成功。
最棒的subprocess
是你可以让它收集来自stdout
and的所有文本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:
# try to run from /usr/local/bin
return _run_cmd("/usr/local/bin/lumberjack", tup_args)
except OSError:
pass
try:
# try to run from /opt/forest/bin
return _run_cmd("/opt/forest/bin/lumberjack", tup_args)
except OSError:
pass
try:
# try to run from "bin" directory in user's home directory
home = os.getenv("HOME", ".")
s_cmd = home + "/bin/lumberjack"
return _run_cmd(s_cmd, tup_args)
except OSError:
pass
# Python 3.x syntax for raising an exception
# for Python 2.x, use: raise OSError, "could not find lumberjack in the standard places"
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).
"""
# build a list to pass to subprocess
lst_cmd = [None] # dummy arg to reserve position 0 in the list
lst_cmd.extend(args) # arguments come after position 0
for path in locations:
# It's legal to put a callable in the list of locations.
# When this happens, we should call it and use its return
# value for the path. It should always return a string.
if callable(path):
path = path()
# put full pathname of cmd into position 0 of list
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, # specify callable that returns user's home directory
]
return try_alternatives("lumberjack", locations, args)
run_lumberjack("-j")