要約
のいずれの方法で実行した場合にも sys.argv[1:] には [arg1, arg2, ...] が格納される
シェル芸勉強会の課題
シェル芸勉強会が開催されていたのですが参加できなかったので、Twitter に流れてきた問題を解いてみました。
Q2
— Ryuichi Ueda (@ryuichiueda) 2018年3月17日
`seq 10` から開始して、1から10までの数を足し算する
方法をなるべく多く考えてみましょう。#シェル芸
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 にして足せばいいので
seq 10|xargs python -c "import sys;print(sum(map(int,sys.argv[1:])))" #シェル芸
— Yoichi Nakayama (@yoichi22) 2018年3月17日
— シェル芸bot (@minyoruminyon) 2018年3月17日
で解くことができました。
理解不足
解けはしたものの、一晩明けて何か引っかかる気がしたので見直してみると、'-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] が上書きされる。