理系学生日記

おまえはいつまで学生気分なのか

シェルスクリプトでテンプレート処理 (envsubstの使い方)

シェルスクリプトで、テンプレート処理を行いたいというケースがあります。

ここでいうテンプレート処理について、以下のようなテンプレート(ここでは template.tt と呼びます)があった場合を例示します。

property1=${hoge}
property2=${fuga}

以下のように template.tt を処理します。

Template->new('template.tt')->process( {
  hoge => 'aaa',
  fuga => 'bbb'
})

結果として以下を得ます。このような処理をテンプレート処理と呼んでいます。

property1=aaa
property2=bbb

こういう処理を実現することは、プログラミング言語に限らず必須の機能なので、どの言語にもだいたいライブラリなり言語としての実装なりが入っています。 が、シェルスクリプトでこれを実現したくなりました。 具体的には、Vagrant の provisioning を行うときなど、Ansible 使うほどでも無いけど設定ファイルをテンプレートとして作りたいんだよなぁというユースケースが生じたわけです。

envsubst が使える子

こういうユースケースにおいては、envsubst コマンドができる子でした。gettext パッケージに入っているので、以下でインストールが可能です。

$ yum install gettext

使い方

envsubst コマンドはその名前の通り、環境変数を置き換える形でテンプレート処理を実現します。テンプレート処理といっても、いわゆる反復や条件分岐などは実現できない。単純に置換処理のみを行えます。

この置換処理、ヒアドキュメント等を駆使すればもちろん不可能ではないのですが、なかなか面倒なのですよ。シングルクオテーションで括られている箇所の置換とか、考えただけでもう…。

というわけで前置きが長くなりましたが、具体的に使ってみましょう。百聞は一見にしかずなので、まずは具体例から。

$ export hoge=aaa fuga=bbb piyo=ccc
$ cat <<'EOF' | envsubst
property1=${hoge}
property2=${fuga}
property3='$piyo'
EOF

この出力はこうなります。直感的ですね。

property1=aaa
property2=bbb
property3='ccc'

このように envsubst コマンドは、標準入力から流し込まれたテキストの中で $VAR${VAR} という形で出現する文字列を環境変数 VAR の値で置き換えるという機能を持っています。シングルクオートの中だろうが、問題なく値置換が行われます。

しかし、このように「全部の $VAR 形式の箇所を置換する」だけでは、現実的にうまくいかないケースっていうのもあるんですよね。 例えば以下のように、テンプレート処理後のテキストを /etc/profile.d/maven.sh として配置することを想定したファイルがあるとします。

$ cat maven.sh.tmpl
#!/bin/bash
export M2_HOME=$M2_HOME
export PATH=$M2_HOME/bin:$PATH

これ、無造作に全部の $VAR の箇所を置き換えちゃうと、PATH の値がおかしなことになっちゃうんですよね。

$ export M2_HOME=/usr/local/maven PATH=/bin:/usr/bin:/usr/local/bin
$ cat maven.sh.tmpl | envsubst
#!/bin/bash
export M2_HOME=/usr/local/maven
export PATH=/usr/local/maven/bin:/bin:/usr/bin:/usr/local/bin
# 単に PATH に /usr/local/maven/bin を追加したかっただけなのに…

こういうとき、envsubst は、「特定の $VAR だけを置き換える」ということを実現してくれます。envsubst の引数に、置換したい $VAR 形式を記述するわけですね。

$ export M2_HOME=/usr/local/maven PATH=/bin:/usr/bin:/usr/local/bin
$ cat maven.sh.tmpl | envsubst '$M2_HOME'
#!/bin/bash
export M2_HOME=/usr/local/maven
export PATH=/usr/local/maven/bin:$PATH  # これがしたかった

というわけで、単純な置換のみだけであれば、envsubst は十分使い物になっています。