説明
たった4行のシェルスクリプトである。
#!/bin/sh
sh <<__EOT1__
cat <<__EOT2__
cat "$@"
何一つインストールされていなくて、sh コマンドと cat コマンドだけは動く、そんな環境で使う。
例えば Docker に最適。
コマンドライン引数で指定されたファイル群をヒアドキュメントとして解釈させる。
シェルスクリプトのヒアドキュメントとして展開できるものがすべて通用する。
- 環境変数展開 (
$環境変数名
、または${環境変数名}
) - コマンド展開 (
$(コマンド)
、または`コマンド`
)
シェル変数は子プロセスに伝播しないので使えない。export で環境変数にしなければならない。
ヒアドキュメントのため、実行制御機能は持たない。つまり、条件判断、繰り返しなどは行わない。
(コマンド展開による簡易的な条件判断と繰り返しは可能。)
テンプレート側では、ヒアドキュメントで展開対象となる以下の文字群はあらかじめ \
(U+005c バックスラッシュ)でエスケープしておく。
-
$
(U+0024) -
\
(U+005c) -
`
(U+0060)
ヒアドキュメントの終端に __EOT1__
, __EOT2__
が使われているが、確実にファイル内容や変数と衝突しないような文字列を入れればよい。例えば UUID あたりの方がよい。
ファイル終端がヒアドキュメントの終端として扱われるので、実は終端は書かなくてよい。(ここがミソ)
なお、コマンドがネストするのでパフォーマンスはよくない。
本来は #!/bin/sh
は要らないはずだが、外すと bash が sh をエミュレートしている環境でヒアドキュメントの終端不足の警告が出てくるので付けている。
./render_template.sh: line 4: warning: here-document at line 2 delimited by end-of-file (wanted `__EOT1__')
仕組み
テンプレートファイル
$HELLO
実行
$ HELLO="Hello, world!" ./render_template.sh hello.tmpl
最初の sh に対するヒアドキュメントとして以下が展開される。
cat <<__EOT2__
`cat "$@"`
cat <<__EOT2__
$HELLO
次に cat に対するヒアドキュメントとして以下が展開される。
$HELLO
Hello, world!
つまり二重にヒアドキュメントを解釈させるところがミソ。
テスト
テンプレートファイル
Hello, world!
"Hello, world!"
$HELLO
\$HELLO
\\$HELLO
$(echo "Hello, world!")
\$(echo "Hello, world!")
`echo "Hello, world!"`
\`echo "Hello, world!"\`
\\\`echo "Hello, world!"\\\`
実行
$ HELLO="Hello, env!" ./render_template.sh test.tmpl
Hello, world!
"Hello, world!"
Hello, env!
$HELLO
\Hello, env!
Hello, world!
$(echo "Hello, world!")
Hello, world!
`echo "Hello, world!"`
\`echo "Hello, world!"\`
参考
- 簡易テンプレートエンジン extcat coneta #1 https://qiita.com/EjiriAkira/items/30bfb2520744094a3679
- Shellでファイルテンプレート的な機能を実現 https://qiita.com/jerrywdlee/items/e99c519ae2af96d89985
- 低機能でポータブルなテンプレートエンジンが欲しい→そこでenvsubstですよhttps://qiita.com/hnw/items/3d8bad7852510c3a84d7