鯨飲馬食

いろいろつまみ食いで勉強したことのメモ書き

Python のコマンドライン引数

要約

  • python module.py arg1 arg2 ...
  • python -m module arg1 arg2 ...
  • python -c script_str arg1 arg2 ...

のいずれの方法で実行した場合にも sys.argv[1:] には [arg1, arg2, ...] が格納される

シェル芸勉強会の課題

techplay.jp

シェル芸勉強会が開催されていたのですが参加できなかったので、Twitter に流れてきた問題を解いてみました。

seq は 1 から指定した数までインクリメントしながら一行ずつ出力します

$ seq 10
1
2
3
4
5
6
7
8
9
10

この出力を xargs に食わせると

$ seq 10|xargs echo
1 2 3 4 5 6 7 8 9 10

のようにコマンドライン引数にできるので、

$ seq 10|xargs python -c "import sys;print(sys.argv)"
['-c', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']

のようにすれば Python にリストを受け渡せることがわかります。あとは argv[1:] を int にして足せばいいので

で解くことができました。

理解不足

解けはしたものの、一晩明けて何か引っかかる気がしたので見直してみると、'-c' が先頭の引数になってるけどその後に与えたスクリプト文字列 "import sys;print(sys.argv)" はどこへいったのか、全然理解できてないじゃないかということに気付きました。

試しに他のやり方で同じ内容の処理を実行するとどうなるか見てみると

~/argv-test$ cat sum.py
import sys
print(sys.argv)
~/argv-test$ seq 10|xargs python sum.py
['sum.py', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
~/argv-test$ seq 10|xargs python -m sum
['/Users/yoichi/argv-test/sum.py', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']

いずれのやり方でも、xargs 経由で与えた引数は2要素目以降に入っています。

公式ドキュメント https://docs.python.jp/3/library/sys.html#sys.argv を参照すると、

Pythonスクリプトに渡されたコマンドライン引数のリスト。 argv[0] はスクリプトの名前となりますが、
フルパス名かどうかは、OSによって異なります。コマンドライン引数に -c を付けて Pythonを起動した
場合、 argv[0] は文字列 '-c' となります。スクリプト名なしでPythonを起動した場合、 argv[0] は
空文字列になります。

とその仕様について書かれていました。スクリプトの実行方法に依らずに argv[1:] でコマンドライン引数が取れるよう、この仕様になっているのだと思います。

実装確認

"-c", "-m" を指定した場合の動作を CPython の実装で確認しました。

  • Modules/main.c の Py_Main() で "-c" あるいは "-m" を argv[0] として PySys_SetArgv() に渡しており、Python/sysmodule.c では一旦そのまま sys.argv に格納している。
  • "-m" が指定されていたときには続いて Modules/main.c の RunModule() が呼ばれ、 runpy.py の _run_module_as_main() を実行し、そこで sys.argv[0] が上書きされる。