20

call() を使用して Python スクリプトにコマンドを実行させる方法を理解しました。

import subprocess

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

これは機能しますが、問題があります。ユーザーのコマンド パスに lumberjack が含まれていない場合はどうなるでしょうか。lumberjack が Python スクリプトと同じディレクトリに配置されていれば機能しますが、スクリプトは lumberjack を探す必要があることをどのように認識しているのでしょうか。command-not-found エラーが発生した場合、ランバージャックはコマンド パスに存在しないと考えました。スクリプトはそのディレクトリが何であるかを把握し、そこにあるランバージャックを探し、最後にユーザーにランバージャックを次のいずれかにコピーするよう警告します。いずれかで見つからなかった場合は、これらの 2 つの場所。エラー メッセージの内容を確認するにはどうすればよいですか? check_call() がエラー メッセージと returncode 属性に関する何かを返す可能性があることを読みました。check_call() と returncode の使用方法、メッセージの内容、メッセージが command-not-found かどうかを確認する方法の例が見つかりませんでした。

私はこれについて正しい方法で進んでいますか?

4

3 に答える 3

26

簡単なスニペット:

try:
    subprocess.check_call(['executable'])
except subprocess.CalledProcessError:
    pass # handle errors in the called executable
except OSError:
    pass # executable not found
于 2012-12-31T23:30:30.797 に答える
6

subprocessOSErrorコマンドが見つからない場合、例外が発生します。

コマンドが検出され、コマンドがsubprocess実行されると、コマンドから結果コードが返されます。標準では、コード 0 は成功を意味し、失敗はゼロ以外のエラー コードです (これはさまざまです。実行している特定のコマンドのドキュメントを確認してください)。

なので、キャッチすればOSError存在しないコマンドを扱えるし、リザルトコードを見ればコマンドが成功したかどうかがわかります。

の優れた点は、とsubprocessからすべてのテキストを収集できることです。その後、好きなように、それを破棄したり、返したり、ログに記録したり、表示したりできます。コマンドが失敗してテキストが出力されない限り、コマンドからのすべての出力を破棄するラッパーをよく使用します。stdoutstderrstderr

ユーザーに実行可能ファイルをコピーするように頼むべきではないことに同意します。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")

編集:少し考えた後、上記を完全に書き直すことにしました。場所のリストを渡すだけで、ループが機能するまで別の場所を試す方がはるかにクリーンです。しかし、必要がなければユーザーのホーム ディレクトリの文字列を作成したくなかったので、callable を代替のリストに入れることを正当化しました。これについて質問がある場合は、質問してください。

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")
于 2012-12-31T23:24:16.900 に答える
2

うわー、それは速かったです!Theodros Zelleke の簡単な例と steveha の関数の使用を、OSError に関する abarnert のコメントとファイルの移動に関する Lattyware のコメントと組み合わせました。

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/Snow Leopard)、Debian (Squeeze)、および Windows (7) でテストしました。3 つのオペレーティング システムすべてで、希望どおりに動作するように見えました。check_call と CalledProcessError を使用してみましたが、何をしても毎回エラーが発生するようで、スクリプトでエラーを処理できませんでした。スクリプトをテストするために、スクリプトのディレクトリに lumberjack があったので、名前を「lumberjack」から「deadparrot」に変更しました。

このスクリプトの書き方に問題はありますか?

于 2013-01-01T04:27:59.187 に答える