発端
rubyの開発をする時、.erb
などでテンプレートを記入して、いざ使う時変数展開(Rendering)すれば柔軟に対応できる。
しかしだいたいこういう機能はRuby、PythonやJavaScriptのような高水準言語でしか実装出来ない。Dockerのような極小システムを使う時、わざわざRubyをインストールするのもだるいし、Shellでなんとか対応してみた。
2019-2-15追記
@hnw さんがご指摘した通り、これはあくまでshellだけ作動する環境での一時対策であり、nginx:alpine
などDockerのイメージを使う場合は素直にenvsubstを使った方が効率的である。
TL;DR
コアなファンクションは下記の四行です
while read line || [ -n "${line}" ]
do
echo $(eval echo "''${line}")
done < ./template.txt
上記template.txt
の中身は:
This is ${USER}\\'s computer.
Home directory is ${HOME}.
Rendering結果は:
This is jerrylee's computer.
Home directory is /Users/jerrylee.
解説
原理
template.txt
の内容を一行ずつ読み取り、${USER}
のようなところを環境変数の該当変数で展開して吐き出す。
'
などShellでの制御文字は \\'
でエスケープする。いわば変数の二重展開である。
ここの変数の二重展開も少し特別のもので、ネット上は該当する記事は見つかっておらず、試行錯誤でやっとたどり着いたもの。
ネット上に出されたパターンをだいたい試してみた、結果はこちら。
echo $line
# `# File Template Demo` => `# File Template Demo`
# `home: ${HOME}` => `home: ${HOME}`
echo $(eval echo '$'${line}) # OR `echo $(eval echo '$'$line)`
# `# File Template Demo` => `0 File Template Demo`
# `home: ${HOME}` => `: /Users/jerrylee`
echo $(eval echo "'$'${line}")
# `# File Template Demo` => `$# File Template Demo`
# `home: ${HOME}` => `$home: /Users/jerrylee`
echo $(eval echo "${line}")
# `# File Template Demo` => `` # 改行だけでる、コメントアウトが効いたと思う
# `home: ${HOME}` => `home: /Users/jerrylee`
echo $(eval echo "''${line}")
# `# File Template Demo` => `# File Template Demo`
# `home: ${HOME}` => `home: /Users/jerrylee`
readline
別に一気にファイルを読み込んで処理しても構わないが、2箇所つまずく場所がある。
- 改行、ファイルの改行は変数展開の時で予想外な動きをする可能性がある
- メモリ使用量、大きなファイルを処理する時、無駄にメモリを使うのはよくない
|| [ -n "${line}" ]
この構文を追加すれば、仮にテンプレートの最後に改行がなくても、最後の行まで読み取れる。
実用例
#!bin/sh
if [ ! -f $1 ]; then
echo "Usage:\n$ sh render-console.sh template.tpl > target.txt"
exit 1
fi
while read line || [ -n "${line}" ]
do
echo $(eval echo "''${line}")
done < $1
My name is ${MY_NAME}
上記 render.sh
と template.txt
を作成したら、以下コマンドを実行する。
$ export MY_NAME=Alice; sh render.sh template.txt > result.txt
MY_NAME
が定義され、result.txt
が生成される。
My name is Alice
余談
改めてShell Script はただのオバハンではないことがわかった の表現力に驚いた。
参考
https://askubuntu.com/a/121868
https://qiita.com/kod314/items/f8aa4929501882e97b38
https://orebibou.com/2015/01/シェルスクリプトでevalコマンドを用いた変数の2重/
https://qiita.com/Ets/items/a7fa24b138b8ee883dac
https://www.server-memo.net/shellscript/read-file.html
https://qiita.com/hnw/items/291090f8632f3b40e1a0