template_engine.sh bashテンプレートエンジン coneta #5


template_engine.sh

 bashで使うテンプレートエンジン1です。

機能

  • 「テンプテートファイル」の「被置換文字列」を「置換文字列」で置換後、「出力方法」に従って、結果を出力します。
  • 「テンプテートファイル」、「被置換文字列」、「置換文字列」、「出力方法」は「データファイル」で定義します。
  • 「データファイル」に任意のbashコマンドを記述(※)することにより、テンプレートエンジンの振る舞いを変更することがでます。
    ※ 1行で記述する必要がある。複数のコマンドを記述する場合は;を使用します。
      コマンドはbashevalで評価されます。
  • 「データファイル」の読み込み、「テンプレートファイル」の読み込み、「出力」はデフォルトでcatコマンドを使用していますが、環境変数にコマンドを設定することにより、別のコマンドに変更することができます。
    ※ この機能はextcatを使うことを想定して実装しましたが、他のコマンドを使うことも可能です。extcatは本スクリプトに組み込んであります。
No. 環境変数 対象となる機能
1 CAT_DATA データファイルの読み込み
2 CAT_TEMPLATE テンプレートファイルの読み込み
3 CAT_OUTPUT 出力

使用方法

【実行方法】
【データファイルを引数で指定する】
 template_engine.sh [file [file]...]

【データファイルを標準入力に流し込む】
  cat file | template_engine.sh
【データファイルの書式】
データファイル
CMD bashコマンド 
DEF テンプレートファイル 被置換文字列1△被置換文字列2△被置換文字列n
DAT 出力方法            置換文字列1△置換文字列2△置換文字列n

CMD行:任意、複数行可。
DEF行:必須、以降のDAT行の被置換文字列、複数行可。
DAT行:必須、直前のDEF行の置換文字列、複数行可。
出力方法:
  > ファイル #ファイルに上書き
  >> ファイル #ファイルに追加
△:デフォルトはタブコード(0x09)です。環境変数に設定することにより、別の文字に変更することができます。

No. 環境変数 対象となる機能
1 DAT_IFS DAT行の読み込み
2 DEF_IFS DEF行の読み込み

被置換文字列:sedコマンドの正規表現
置換文字列:シングルクォート(0x27)とタブコード(0x09)以外の文字はそのまま記述します。
  シングルクォート(0x27)は以下を記述します。
     '\\\\\\\\''
  タブコード(0x09)はCMD行で環境変数にタブコードを設定し、DAT行でその環境変数を参照します。
     export XTAB='△' △はタブコード、環境変数XTABは任意です、他の名前でも可。
     '\\\\\\\"${XTAB}"'

【関連図】


※図中のsedのsコマンドは区切り文字に/を使用していますが、実際は0x04を使用しています。

関連図のdotソース
digraph xxx{
    node[shape=record fontname="MS Gothic"]
    TEMPLATE_ENGINE[label="template_engine.sh データファイル"]
    DATA_FILE
         [label="{ データファイル|{CMD| bashコマンド                 }|{DEF| テンプレートファイル| 被置換文字列(複数可) }|{DAT| 出力方法\l >出力ファイル \l>>出力ファイル   \l| 置換文字列(複数可)  }}"];
    TEMPLATE_FILE[label=" テンプレートファイル"]
    OUTPUT[label=" 出力ファイル"]
    edge [arrowhead=open style=dotted]
    DATA_FILE:f1->TEMPLATE_FILE
    DATA_FILE:f2->OUTPUT
    TEMPLATE_ENGINE->DATA_FILE
    edge [arrowhead=normal style=solid]
    TEMPLATE_FILE->OUTPUT[ fontname="MS Gothic" label="sed s/被置換文字列/置換文字列/g" color="green"]
}

実行例

【テンプレートファイル】
template_file.txt
########################################
# @1@ @2@
# @2@ @1@
【データファイル】
data.txt
CMD     rm result.txt 2> /dev/null
DEF     template_file.txt       "@1@"   "@2@"
DAT     >>result.txt    "DATA 11        DATA21
DAT     >>result.txt    "DATA 12        ${HOME}DATA22
DAT     >>result.txt    "DATA 13'\\\\\\\\''     DATA23
CMD     export XTAB='   '
DAT     >>result.txt    "DATA 14'\\\\\\\"${XTAB}"'      DATA24
DAT     >>result.txt    DATA 15!"#$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~@@@ DATA25
【実行】
template_engine.sh data.txt 
【結果】
result.txt
########################################
# "DATA 11 DATA21
# DATA21 "DATA 11
########################################
# "DATA 12 ${HOME}DATA22
# ${HOME}DATA22 "DATA 12
########################################
# "DATA 13' DATA23
# DATA23 "DATA 13'
########################################
# "DATA 14       DATA24
# DATA24 "DATA 14 
########################################
# DATA 15!"#$%@1@()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~@@@ DATA25
# DATA25 DATA 15!"#$%@1@()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~@@@

実装

template_engine.sh
#!/bin/bash
extcat () { cat $@ | bash <<< "cat <<< "\""$(cat)"\""" ; };export -f extcat
export CAT_DATA=${CAT_DATA:-"cat"}
export CAT_TEMPLATE=${CAT_TEMPLATE:-"cat"}
export CAT_OUTPUT=${CAT_OUTPUT:-"cat"}
export DEF=()
export CMD
export DAT
export TEMPLATE_FILE
export OUTPUT
export TAB_CODE=$(printf "\t")
main_proc(){
    local line kind rest
    ${CAT_DATA} $@ | sed '/^#/d' |  while read line
    do
        read kind rest <<< "${line}"
        case "${kind}" in
            DEF)    func_DEF "${rest}";;
            DAT)    func_DAT "${rest}";;
            CMD)    func_CMD "${rest}";;
            *)
        esac
    done
}
func_DEF(){
    local line old_ifs rest i d
    line=$1
    old_ifs=${IFS}
    IFS=${DEF_IFS:-${TAB_CODE}}
    read TEMPLATE_FILE rest <<< "${line}"
    i=0
    unset DEF[@] 
    for d in ${rest}
    do
        eval DEF[i]="${d}"
        ((i+=1))
    done
    IFS=${old_ifs}
}
func_DAT(){
    local line old_ifs rest i d
    local sed_s_command_delimiter=$(echo -n -e '\x04')
    line=$1
    old_ifs=${IFS}
    IFS=${DAT_IFS:-${TAB_CODE}}
    read OUTPUT rest <<< "${line}"
    i=0
    {
        printf "${CAT_TEMPLATE} ${TEMPLATE_FILE} | sed '\n"
        for d in ${rest}
        do
            printf 's'${sed_s_command_delimiter}'%s'${sed_s_command_delimiter}'%s'${sed_s_command_delimiter}'g\n' "${DEF[i]}" "${d}"
            ((i+=1))
        done
        printf "' | ${CAT_OUTPUT} ${OUTPUT}\n"
    } | bash
    IFS=${old_ifs}
}
func_CMD(){
    eval "$1"
}
main_proc $@

環境

ホスト Windows10 COREi7
VM   VirtualBox バージョン 5.2.8 r121009 (Qt5.6.2)
     CentOS Linux release 7.4.1708 (Core)
     3.10.0-693.21.1.el7.x86_64 #1 SMP Wed Mar 7 19:03:37 UTC 2018
GNU bash, バージョン 4.2.46(2)-release (x86_64-redhat-linux-gnu)



  1. wikipediaによると、テンプレートエンジンはテンプレートと呼ばれる雛形と、あるデータモデルで表現される入力データを合成し、成果ドキュメントを出力するソフトウェアまたはソフトウェアコンポーネントである。」とのことです。