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:
# 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")
'check_call()'應該引發一個錯誤,如果命令沒有乾淨地退出(這是預期的不存在的命令) – inspectorG4dget
另外,我建議不告訴用戶複製可執行文件,除非作爲最後的手段。軟件包管理者幾乎總是安裝東西的最佳選擇。 –
@DaveBrunker:你對'call'和'check_call'有點困惑。 'call'已經給你返回代碼 - 不是作爲一個屬性,就像函數的返回值一樣。使用'check_call'的原因是它負責檢查你的返回代碼,引發一個'CalledProcessError'。任何一個人都會爲您找到一個「OSError」(或類似的,取決於您的Python版本),以便找不到該程序。 – abarnert