HOWTO · Python
OSError: [Errno 8] Python の Exec フォーマット エラー
このチュートリアルでは、Python で OSError: [Errno 8] Exec format error を修正する方法を説明します。
このページの内容
Linux で Python がオペレーティングシステムにファイルの直接実行を要求したとき、認識可能な実行形式でなければ OSError: [Errno 8] Exec format error が発生することがあります。よくある例は、インタープリターを指定する行(シバン)のないシェルスクリプトです。これはオペレーティングシステムによる起動時のエラーであり、Python の構文エラーではありません。以下では subprocess モジュールを使い、スクリプトの先頭に #!/bin/sh を追加する方法と、sh を明示的に呼び出す方法を紹介します。
OSError: [Errno 8] Exec format error が発生する理由
subprocess.run([script_path]) のように直接実行すると、Linux は対象ファイルを調べ、実行方法を判断します。スクリプトには、利用可能なインタープリターを指定する有効なシバンが必要です。コンパイル済みプログラムの場合は、システムで実行可能な形式とアーキテクチャである必要があります。Linux がファイルを処理する方法を特定できないと、Python に OSError: [Errno 8] Exec format error が通知されます。
これは権限エラーとは異なります。以下の再現例では、オペレーティングシステムが実行形式を確認する段階まで進むため、スクリプトに実行権限が必要です。権限がなければ、直接実行は形式の確認より先に権限エラーで失敗することがあります。実行権限を追加するだけでは、シバンの欠落も未対応の実行形式も解決しません。
シェルスクリプトでエラーを再現する
次の Linux の例では、シバンのない実行可能なシェルスクリプトを作成し、それを直接起動します。起動時の失敗を捕捉するため、例のプログラム自体は正常に終了できます。自分の呼び出しを調べるときは failure の内容を確認してください。
コード内に埋め込まれたメッセージ Hello from the script は英語のままです。
import subprocess
import tempfile
from pathlib import Path
with tempfile.TemporaryDirectory() as directory:
script = Path(directory) / "hello.sh"
script.write_text("printf 'Hello from the script\\n'\n", encoding="utf-8")
script.chmod(0o755)
failure = None
try:
subprocess.run([str(script)], check=True, capture_output=True, text=True)
except (OSError, subprocess.CalledProcessError) as error:
failure = error
一時ファイルには実行権限があり、内容も有効なシェル構文ですが、先頭行にインタープリターの指定がありません。errno とエラーメッセージの正確な内容はオペレーティングシステムによって異なります。ここでの説明は、前述の Linux のケースを対象としています。
スクリプトにシバンを追加する
コマンドより前、ファイルの先頭に #!/bin/sh を記述します。オペレーティングシステムは、直接実行するときにこのインタープリター指定を読み取ります。必ず1行目に置き、ファイルをプレーンテキストとして保存し、/bin/sh が利用可能であることを確認してください。次の例では、シバンを付けた新しいスクリプトを作成し、直接実行します。
次の例を実行してください。
このセクションのコードと表示されるラベルや出力は英語のままです。
import subprocess
import tempfile
from pathlib import Path
with tempfile.TemporaryDirectory() as directory:
script = Path(directory) / "hello.sh"
script.write_text("#!/bin/sh\nprintf 'Hello from the script\\n'\n", encoding="utf-8")
script.chmod(0o755)
result = subprocess.run([str(script)], check=True, capture_output=True, text=True)
print(result.stdout, end="")
Hello from the script
この方法ではスクリプトを直接実行しつつ、使用するインタープリターをスクリプト内で明示できます。POSIX シェル以外の機能を使う場合は、その機能に対応し、指定したパスにインストールされているシェルをシバンで指定してください。出力例に表示される Hello from the script は、コードと同じく英語のままです。
インタープリター経由でスクリプトを実行する
別の方法として、インタープリターを実行ファイルに指定し、引数としてスクリプトのパスを渡せます。この方法はスクリプトのシバンに依存しません。Linux が sh を直接起動し、sh がファイルを読み取るためです。スクリプトを変更できない場合や、呼び出し側でインタープリターを選びたい場合に便利です。ただし、指定したインタープリターとスクリプトのパスが存在する必要があります。
次の自己完結した例でも、同じシェルコマンドと一時ファイルの手順を使います。
このセクションのコードと表示されるラベルや出力は英語のままです。
import subprocess
import tempfile
from pathlib import Path
with tempfile.TemporaryDirectory() as directory:
script = Path(directory) / "hello.sh"
script.write_text("printf 'Hello from the script\\n'\n", encoding="utf-8")
result = subprocess.run(["sh", str(script)], check=True, capture_output=True, text=True)
print(result.stdout, end="")
Hello from the script
引数をリストで渡すと、subprocess はスクリプトのパスを1つの引数として sh に渡します。信頼できない入力からシェルコマンド文字列を組み立てるのは避けてください。引数リストを使えば、実行ファイルとその引数を分けて渡せます。出力例の Hello from the script は、コードと同じく英語のままです。
スクリプトと実行形式を確認する
起動時の問題を特定するには、次の点を確認してください。
subprocessに渡しているパスが、目的のファイルを指していることを確認します。通常、パスが存在しない場合は実行形式エラーではなく、ファイルが見つからないというエラーになります。- 先頭行を確認します。
/bin/shで実行するシェルスクリプトなら、1行目は正確に#!/bin/shである必要があります。この指定より前に別の内容があってはいけません。バイトオーダーマークやその他のバイトが先頭にあると、オペレーティングシステムが1行目のシバンとして認識できない場合があります。 - シバンで指定したインタープリターが存在し、現在のシステムで実行できることを確認します。インタープリターを明示する方法でも、インストール済みのものを指定してください。
- 対象がコンパイル済み実行ファイルなら、現在のオペレーティングシステムとプロセッサアーキテクチャ向けにビルドされているか確認します。形式に対応していない場合は、対象環境向けに再ビルドするか、互換性のある実行ファイルを使います。
- 直接実行では実行権限も別途必要です。権限がない場合は、その権限の問題を個別に解決してください。権限を変更しても、認識できない実行形式は修正されません。
これらの確認により、無効または認識されない実行形式、誤ったパス、権限の問題を区別できます。修正方法は、スクリプトの起動方法に合わせて選んでください。