Call()を使用してpythonスクリプトを実行してコマンドを実行する方法を理解しました。
import subprocess
mycommandline = ['lumberjack', '-sleep all night', '-work all day']
subprocess.call(mycommandline)
これは機能しますが、問題があります。ユーザーがコマンドパスに木こりを持っていない場合はどうでしょうか。 lumberjackがpythonスクリプトと同じディレクトリに置かれていれば動作しますが、スクリプトはどのようにしてlumberjackを探すべきかを判断しますか?木こりはコマンドパスにありません。スクリプトは、そのディレクトリが何であるかを見つけ、そこで木こりを探し、最終的にどちらかに見つからない場合は、これら2つの場所のいずれかに木こりをコピーするようユーザーに警告します。エラーメッセージを調べるにはどうすればよいですか?check_call()がエラーメッセージとreturncode属性に関する何かを返すことがあることを読みました。check_call()とreturncodeの使用方法の例が見つかりませんでした。または、メッセージがcommand-not-foundであるかどうかを確認する方法。
私もこれについて正しい方法で行っていますか?
うわー、それは速かったです! 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」から「deadparrot」に変更しました。
このスクリプトの記述方法に問題はありますか?
簡単なスニペット:
try:
subprocess.check_call(['executable'])
except subprocess.CalledProcessError:
pass # handle errors in the called executable
except OSError:
pass # executable not found
コマンドが見つからない場合、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")