よちよちpython

独習 python/Qpython/Pydroid3/termux/Linux

Pythonコマンドライン引数

コマンドライン引数について。



目次




実行環境


Androidスマホ
termux
Python3.7
JupyterNotebook



コマンドライン引数とはなあに?


Pythonファイルを実行するとき

$ python ファイル名.py


Pythonファイルが実行される。

コマンドライン引数は上の例で言えば「ファイル名」にあたる。

コマンドやプログラムの後にスペースを空けて書かれるパラメータのことで、それが引数としてコマンドやプログラムに渡される。

蛇足ですが(パイソンなだけに…)、
コマンド操作の設計でコマンド(引数)のようなカッコ付きの書き方でも良かったのだろうが、()カッコを入力するのも面倒だということで、スペースを空ければ済むようにしたのだとか。

Python2系のprint()関数は()カッコが付かない。コマンド操作風に入力スピード重視を目指してたんだろうか。



ところで、アプリ使用時にプログラムを書き換えた事あります?


電卓アプリでもブラウザでも何でもいいですが、通常アプリを使うときに直接プログラムのファイルを開いて、じかに変数や引数の値を書くような使い方にはなっていませんよね。一度たりともそんなことしたことない。



だがプログラミングのサンプルは…


入門書によく載ってるような、2値の足し算をするPythonプログラムのサンプルは大体こんな感じ。

# 関数の定義
def add(a,b):
    return a + b

# 実行
print(add(7,5))
12

↑は、引数a,bを直接プログラム内に書き込むような作りになっている。

もし電卓アプリがこんな風になっていたら、計算する度にプログラムファイルを開いて書き換えなければならなくなる。面倒くさくて誰も使わないよね…



プログラムファイルを開かず引数を渡す知恵


誰か賢い人が計算装置とデータを切り分けた。いや、むしろコンピュータは元々機械式で歯車を動かして計算してたので、データは最初から計算と分かれていただろう。知らんけど。算盤もデータは別にあるし。

それはともかく、コマンドライン引数に話しを戻そう。



Pythonコマンドライン引数の使い方


コマンドライン引数の取得方法


コマンドライン引数を取得するだけのPythonコードを書きます。ターミナルで

python ファイル名.py 引数


を実行すると、コマンド引数がファイル名.pyに渡されてPythonファイルが実行される。
(一行目の%%writefile云々 はJupyterNotebookのファイル書き出し用マジックコマンドです。無視OK)

%%writefile get_cmd_line_arg.py
import sys

# コマンドライン引数を取得
cmd_arg = sys.argv

# コマンドライン引数を出力
print('コマンドライン引数 :',cmd_arg)
print("==========================")
print('引数のtype',type(cmd_arg))
print("==========================")
print('引数の要素数',len(cmd_arg))
print("==========================")
print('要素のtype',[type(i) for i in cmd_arg])
Overwriting get_cmd_line_arg.py

import sysして、sys.argvコマンドライン引数を取得できます。



コマンドライン引数を入力してPythonファイルを実行


実行します。
(!コマンド云々はJupyterNotebookのコマンド実行文。ターミナルからの実行は先頭の!を省く)

!python get_cmd_line_arg.py 引数1 HelloWorld! 3
コマンドライン引数 : ['get_cmd_line_arg.py', '引数1', 'HelloWorld!', '3']
==========================
引数のtype <class 'list'>
==========================
引数の要素数 4
==========================
要素のtype [<class 'str'>, <class 'str'>, <class 'str'>, <class 'str'>]

実行しました。
リスト型です。
中身は全部文字列のstr型

ファイル名の後に入力したもの以外に、indexの0 = Pythonファイル名まで引数のリストに加わっています。
OSによってフルパス/ファイル名かの出力が変わるらしい。



コマンドライン引数を使って足し算するPythonプログラム


注意点


コマンドライン引数は

  • リスト型
  • リストの先頭はPythonファイル自体
  • リストの各要素はstr型



コマンド引数の要素の取り出しは通常のリストと同じく

  • [index番号]で指定するか
  • スライス[1:2]等を使う



str型の数の足し算+に注意

  • しようとすると1+2=12とかになる。

  • str型をint型かfloat型に変換すること

  • list(map(int,要素がstr型のリスト))を使うとstr型要素をint型要素のリストに変換できる



sum()関数は

  • リストかタプル型を与えると自動で中身を全部足してくれる
  • str型要素の配列を渡すとTypeErrorでstr型の足し算はサポートしていないと言われる
#str型の整数足し算モドキ
a = "1"
b = "2"
print("str型のa={}\nstr型のb={}\nstr型のa+b={}".format(a,b,a+b))

print("===================")

#sum()関数の使い方
num_list = [5,2,8]
print("num_list",num_list)
print("sum(num_list)",sum(num_list))

print("===================")
#リスト要素をstr型に変換する
str_num_list = list(map(str,num_list))
print("全要素int型のリストnum_list",str_num_list)

#無茶してstr型要素のリストをsum()に代入する
try:
    print(sum(str_num_list))
except TypeError:
    print("だから足せないと言っておろう!")
str型のa=1
str型のb=2
str型のa+b=12
===================
num_list [5, 2, 8]
sum(num_list) 15
===================
全要素int型のリストnum_list ['5', '2', '8']
だから足せないと言っておろう!



以上を踏まえて足し算プログラム作成


%%writefile plus.py

import sys


# コマンド引数要素をint型に変換
def conv_int(cmd_params):
    int_params = list(map(int,cmd_params))
    return int_params


# コマンド引数を足す関数
def plus1_2(int_params):
    a = int_params[0]
    b = int_params[1]
    return a+b

# コマンド引数を全部足す関数
def plus_all(int_params):
    return sum(int_params)

## 実行 ##

# コマンドライン引数の取得
# 引数先頭のファイル名はスライスで除外
cmd_params = sys.argv[1:]
print("コマンド引数",cmd_params)

#コマンド引数が10進法の数字なら
if "".join(cmd_params).isdecimal() == True:
    #要素をint型に変換し
    params = conv_int(cmd_params)
    # 要素の1と2番目の合計
    print('1,2番目の合計',plus1_2(params))
    #全要素合計
    print('全合計',plus_all(params))
    
else:
    print("数字以外の引数が紛れ込んでおるぞ!!")
Overwriting plus.py



足し算ファイル実行


整数のコマンド引数にて実行

!python plus.py 7 8 9 10
コマンド引数 ['7', '8', '9', '10']
1,2番目の合計 15
全合計 34


コマンド引数に曲者を紛れ込ませて実行

!python plus.py 5 11 間者
コマンド引数 ['5', '11', '間者']
数字以外の引数が紛れ込んでおるぞ!!



発展編 argparseモジュール(参考)


上で、コマンドライン引数sys.argvPythonスクリプトで足し算させるために、

  • リストの要素が数字かどうか
  • リスト要素数は何個か
  • リスト要素の型を変換

といったチェックや処理をやりましたが、標準モジュールargparse を使うとそうしたコマンドライン引数の解析や処理がもっと簡単にできるようです。
helpのドキュメントやコマンドオプションの設定などもこれでできるっぽい。地獄のコマンドー必須アイテムではなかろうか…

Python公式ドキュメントargparse

[https://docs.python.org/ja/3/library/argparse.html:title]

argparseの使い方を分かり易くまとめてあります

[https://qiita.com/kzkadc/items/e4fc7bc9c003de1eb6d0:title]



その他おまけ xargsコマンド


別ファイルからコマンドで取得して引数で渡す


データが記入された別ファイルをコマンドで読み取り、コマンドライン引数として渡す方法。



次のようなファイルが準備されているとします。

!cat data.txt
5 6 58
8 3 2

「data.txt」なるファイルに2行、スペースを空けて数字が並んでいる。

catコマンドでファイルを読み取り、xargsコマンドに| パイプでつなぐ。

!cat data.txt | xargs python plus.py
コマンド引数 ['5', '6', '58', '8', '3', '2']
1,2番目の合計 11
全合計 82

次の書き方でもできる。

!xargs -a data.txt python plus.py
コマンド引数 ['5', '6', '58', '8', '3', '2']
1,2番目の合計 11
全合計 82

ファイルを読んで得た値を、次のコマンドの引数に渡すコマンドxargs

参照
[https://www.atmarkit.co.jp/ait/spv/1801/19/news014.html:title]



行数や範囲指定sedコマンドの併用


sedコマンドを使うと開始行と終了行を指定して範囲の行数を取得できる。

  • sed -n '開始行,終了行p' ファイル名」 か、
  • 「cat ファイル名 | sed -n '開始行,終了行p'」



参考
[https://linux.just4fun.biz/?%E9%80%86%E5%BC%95%E3%81%8DUNIX%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89/%E6%8C%87%E5%AE%9A%E3%81%97%E3%81%9F%E7%AF%84%E5%9B%B2%E3%81%AE%E8%A1%8C%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95:title]

同じデータファイルを使って、1行目から1行目まで、つまり1行目のみ読み取って実行してみる。

データファイル data.txt の中身

!cat data.txt
5 6 58
8 3 2

1行目のみ読み取り、足し算実行

!cat data.txt | sed -n '1,1p' | xargs python plus.py
コマンド引数 ['5', '6', '58']
1,2番目の合計 11
全合計 69



以上、終わります。