Code
from IPython import InteractiveShell
InteractiveShell.ast_node_interactivity = 'all'
import session_infoAugust 30, 2024
以下のポストではRでglueパッケージを用いたがそれと同じように、今度はPythonを使ってBashスクリプトの生成を行う。
Rではglueパッケージを使って行うことができる文字列補完は、 Pythonではビルトインの機能であるf-stringやstr.format()を用いる。
フォーマット済み文字列リテラル(f-string)を作成するには、 文字列リテラルの前にfをつける(例: f"{apple}")。 実行するとプレースホルダ({})内の式が評価されて、置換された文字列が得られる。
formatメソッド(str.format())テンプレートとなる文字列型(str)オブジェクトのformat()メソッドを使って、 補完を行うことができる。 位置引数(*args)と名前付き引数(**kwagrs)を使って、プレースホルダを置換できる。
f-stringとは異なりプレースホルダ内は式として評価される訳ではない。 式を書くとエラーになる。
vars(), locals(), globals())スコープ内の変数を辞書で返す関数にはvars()の他にlocals()とglobals()がある。 各関数について関数内と関数外それぞれで実行した場合に、どの変数にアクセスできるかを確認してみる。
# "one", "two"というkeyが辞書になければ、そのkeyに"undefined"という値をセット
def set_ud(d):
for key in ["one", "two"]:
if key not in d:
d[key] = "undefined"
return d
t = "{0:<10}: one is {one}, two is {two}"
# global scopeの変数
one = 1
def check():
# local scopeの変数
two = 2
print(t.format("vars()", **set_ud(vars())))
print(t.format("locals()", **set_ud(locals())))
print(t.format("globals()", **set_ud(globals())))
print("@outside check()")
print(t.format("vars()", **set_ud(vars())))
print(t.format("locals()", **set_ud(locals())))
print(t.format("globals()", **set_ud(globals())))
print("@insite check()")
check()@outside check()
vars() : one is 1, two is undefined
locals() : one is 1, two is undefined
globals() : one is 1, two is undefined
@insite check()
vars() : one is undefined, two is 2
locals() : one is undefined, two is 2
globals() : one is 1, two is undefined
関数内で呼び出すと、locals()ではグローバル変数に、globals()ではローカル変数にアクセスできない。
先のポストの例をPythonを使って実践してみよう。 str.format()を使う場合は以下の様に書ける。
from pathlib import Path
# Bashスクリプトのヘッダ
header = r"""!/bin/bash
set -euC
echo "Process started!"
echo ""
"""
# Bashスクリプトのフッタ
footer = r"""echo "All finished!"
"""
# スクリプトのテンプレート
cmd_template = r"""echo "Processing: '{label}'"
cat {label}.csv | \
awk -F, 'NR > 1 {{
sum1 += $1
sum2 += $2
}} END {{
print "Avg. Sepal.Length:", sum1/(NR-1)
print "Avg. Sepal.Width :", sum2/(NR-1)
}}'
echo ""
"""
# ディレクトリ内のCSVファイル名からファイル拡張子を除く文字列を抽出
labels = sorted([f.stem for f in Path(".").glob("*.csv")])
with open("temp.sh", mode="w") as f:
_ = f.write(header)
for label in labels:
_ = f.write(cmd_template.format(**locals()))
_ = f.write(footer)temp.sh
!/bin/bash
set -euC
echo "Process started!"
echo ""
echo "Processing: 'setosa'"
cat setosa.csv | \
awk -F, 'NR > 1 {
sum1 += $1
sum2 += $2
} END {
print "Avg. Sepal.Length:", sum1/(NR-1)
print "Avg. Sepal.Width :", sum2/(NR-1)
}'
echo ""
echo "Processing: 'versicolor'"
cat versicolor.csv | \
awk -F, 'NR > 1 {
sum1 += $1
sum2 += $2
} END {
print "Avg. Sepal.Length:", sum1/(NR-1)
print "Avg. Sepal.Width :", sum2/(NR-1)
}'
echo ""
echo "Processing: 'virginica'"
cat virginica.csv | \
awk -F, 'NR > 1 {
sum1 += $1
sum2 += $2
} END {
print "Avg. Sepal.Length:", sum1/(NR-1)
print "Avg. Sepal.Width :", sum2/(NR-1)
}'
echo ""
echo "All finished!"f-stringは即時評価されるので、f-stringを返す関数を定義してループ内で評価する。 また、f-string単体で使用すると\をエスケープしなければいけないので、 raw-stringと組み合わせてfr""として使う。
cmd_template = lambda : fr"""echo "Processing: '{label}'"
cat {label}.csv | \
awk -F, 'NR > 1 {{
sum1 += $1
sum2 += $2
}} END {{
print "Avg. Sepal.Length:", sum1/(NR-1)
print "Avg. Sepal.Width :", sum2/(NR-1)
}}'
echo ""
"""
with open("temp.sh", mode="w") as f:
_ = f.write(header)
for label in labels:
_ = f.write(cmd_template())
_ = f.write(footer)temp.sh
!/bin/bash
set -euC
echo "Process started!"
echo ""
echo "Processing: 'setosa'"
cat setosa.csv | \
awk -F, 'NR > 1 {
sum1 += $1
sum2 += $2
} END {
print "Avg. Sepal.Length:", sum1/(NR-1)
print "Avg. Sepal.Width :", sum2/(NR-1)
}'
echo ""
echo "Processing: 'versicolor'"
cat versicolor.csv | \
awk -F, 'NR > 1 {
sum1 += $1
sum2 += $2
} END {
print "Avg. Sepal.Length:", sum1/(NR-1)
print "Avg. Sepal.Width :", sum2/(NR-1)
}'
echo ""
echo "Processing: 'virginica'"
cat virginica.csv | \
awk -F, 'NR > 1 {
sum1 += $1
sum2 += $2
} END {
print "Avg. Sepal.Length:", sum1/(NR-1)
print "Avg. Sepal.Width :", sum2/(NR-1)
}'
echo ""
echo "All finished!"str.format()よりは、f-stringの方がRのglue::glue()に近い感じで使える気がする。