2012-12-31 48 views
14

我已經想通了如何使用()調用讓我的python腳本來運行命令:的Python,子,()調用,check_call和返回碼找到,如果一個命令存在

import subprocess 

mycommandline = ['lumberjack', '-sleep all night', '-work all day'] 
subprocess.call(mycommandline) 

該作品但是有一個問題,如果用戶的命令路徑中沒有伐木工人怎麼辦?如果伐木工人與python腳本放在同一個目錄中,它會起作用,但腳本如何知道它應該尋找伐木工人?我想如果有一個命令未發現的錯誤,那麼伐木工人不會在命令路徑中,腳本可以嘗試找出它的目錄是什麼,並在那裏尋找伐木工人,並最終警告用戶將伐木工人複製到其中一個那兩個地方如果沒有找到任何一個。我如何知道錯誤信息是什麼?我讀了check_call()可以返回一個錯誤消息和一些關於returncode屬性的信息。我找不到有關如何使用check_call()和returncode的示例,該消息將會是什麼,或者我可以如何判斷消息是否是命令未找到的。

我是否會以正確的方式去做這件事?

+2

'check_call()'應該引發一個錯誤,如果命令沒有乾淨地退出(這是預期的不存在的命令) – inspectorG4dget

+3

另外,我建議不告訴用戶複製可執行文件,除非作爲最後的手段。軟件包管理者幾乎總是安裝東西的最佳選擇。 –

+1

@DaveBrunker:你對'call'和'check_call'有點困惑。 'call'已經給你返回代碼 - 不是作爲一個屬性,就像函數的返回值一樣。使用'check_call'的原因是它負責檢查你的返回代碼,引發一個'CalledProcessError'。任何一個人都會爲您找到一個「OSError」(或類似的,取決於您的Python版本),以便找不到該程序。 – abarnert

回答

2

哇,這是快!我結合Theodros Zelleke的簡單的例子,steveha的使用功能有關OSERROR和Lattyware的有關移動文件註釋abarnert評論:

import os, sys, subprocess 

def nameandpath(): 
    try: 
     subprocess.call([os.getcwd() + '/lumberjack']) 
     # change the word lumberjack on the line above to get an error 
    except OSError: 
     print('\nCould not find lumberjack, please reinstall.\n') 
     # if you're using python 2.x, change the() to spaces on the line above 

try: 
    subprocess.call(['lumberjack']) 
    # change the word lumberjack on the line above to get an error 
except OSError: 
    nameandpath() 

我測試了它在Mac OS-X(6.8 /雪豹),Debian的(擠壓)和Windows 7的)。它似乎按照我希望在所有三個操作系統上運行的方式工作。我嘗試使用check_call和CalledProcessError,但無論我做了什麼,我似乎每次都會收到錯誤,並且無法獲取腳本來處理錯誤。爲了測試腳本,我將名字從'lumberjack'改爲'deadparrot',因爲我用腳本在目錄中擁有伐木工人。

你看到這個腳本的寫法是否有問題?

4

subprocess將引發異常,OSError,當找不到命令。

找到命令後,subprocess爲您運行命令,結果代碼從命令返回。標準是代碼0意味着成功,任何失敗都是一些非零的錯誤代碼(它有所不同;檢查你正在運行的特定命令的文檔)。

所以,如果你抓住OSError你可以處理不存在的命令,如果你檢查結果代碼,你可以找出命令是否成功。

偉大的事情有關subprocess的是,你可以把它收集所有的文字從stdoutstderr,然後你就可以將其丟棄或退回,或記錄,或你喜歡顯示它。我經常使用一個封裝器來丟棄命令的所有輸出,除非命令失敗,在這種情況下輸出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") 
19

一個簡單的片斷:

try: 
    subprocess.check_call(['executable']) 
except subprocess.CalledProcessError: 
    pass # handle errors in the called executable 
except OSError: 
    pass # executable not found 
相關問題