読者です 読者をやめる 読者になる 読者になる

理系学生日記

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

忍者TOOLS

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

unix technology

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

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

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

これを使って以下のように処理することによって、

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 は十分使い物になると思います。