python -m json.tool を実行したときの動きを確認してみた。
Pythonを起動するとき、-m
をつけて起動するというやり方をする時があります。
この時の動作がどうなっているのかよくわかっていなかったので調べてみました。
$ echo '{"aaa": 123}' | python -m json.tool { "aaa": 123 }
こんな感じで動くやつですね。
今回はこのjson.tool
を題材としています。
なぜ動くのか
そもそもpython -m xxx
はどういう意味なのかというと、
公式ドキュメントを見ると以下のように書かれています。
sys.path から指定されたモジュール名のモジュールを探し、その内容を main モジュールとして実行します。
わかってから読むと、その通りなんですが、 わからない状態で読むとよくわからない文章が書いてあります。
json.toolはどこにある?
__file__
でモジュールのファイルパスを得ることができます。
普通にPythonの中でjson.tool
をimportして調べてみます。
$ python -c 'import json.tool; print(json.tool.__file__)' /opt/homebrew/Cellar/python@3.10/3.10.6_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/json/tool.py
ファイルの場所がわかりました。 実際にそのファイルが存在することも確認できます。
python -m json.tool
でなぜいきなりこれが起動するか
sys.path から指定されたモジュール名のモジュールを探し、その内容を main モジュールとして実行します。
sys.path
からモジュール名を探すとありますので、sys.path
の中身を調べてみます。
$ python -c 'import sys; print("\n".join(sys.path))' /opt/homebrew/Cellar/python@3.10/3.10.6_1/Frameworks/Python.framework/Versions/3.10/lib/python310.zip /opt/homebrew/Cellar/python@3.10/3.10.6_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10 /opt/homebrew/Cellar/python@3.10/3.10.6_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/lib-dynload /opt/homebrew/lib/python3.10/site-packages
1行目が空文字になっているのは、カレントディレクトリを意味しています。 また2行目のzipファイルは、実際にこの場所には存在していませんでした。
これらの場所から、json/tool.py
を探して行きます。
結果として
/opt/homebrew/Cellar/python@3.10/3.10.6_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10
の下にjson/tool.py
が存在しましたのでこのファイルが実行されていたことになります。
これは普通のPythonファイルです。 こんな見慣れたコードが書かれています。
def main(): #(略) if __name__ == '__main__': try: main()
一応以下のように適当なprintを入れると実際に出力が見られました。
if __name__ == '__main__': try: print("呼ばれたよ") main()
$ echo '{"aaa": 123}' | python -m json.tool 呼ばれたよ { "aaa": 123 }
間違いなくこのファイルが呼ばれていることが確認できました。
フルパス指定で動かすとどうなるか
普通のPythonファイルがどこにあるかわかったので、このファイルを直接指定して実行してみます。
$ echo '{"aaa": 123}' | python /opt/homebrew/Cellar/python@3.10/3.10.6_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/json/tool.py 呼ばれたよ { "aaa": 123 }
普通に動きました。 追加したprint文は戻しておきましょう。
ちなみに今回は特に関係ないですが、ファイルを直接指定するのと-m
を指定することの違いとして、
- 直接指定: 実行したPythonファイルが存在するディレクトリが
sys.path
に追加される - -m指定: カレントディレクトリが
sys.path
に追加される
のような違いがあるようです。
まとめ
-m
をつけるとsys.path
からモジュールを探して実行する、
というドキュメントに書かれていることをそのまま検証しただけですが、
実際に手元で確認することでより納得して理解することができました。
蛇足ですが、-m
って何の略なのか?という話ですが、
ドキュメントに<module-name>
と書いてありますので、module
のm
のようです。
__main__
として実行するのでmain
のm
かと思っていました。
誰かの参考になれば幸いです。