ApacheとPHPの構成ファイルの作成を自動化するスクリプトを自分のWebサーバー用に書いています。 CPanelやISPConfigなどのGUIは使用したくありません。
ApacheとPHP構成ファイルのテンプレートがいくつかあります。 Bashスクリプトは、テンプレートを読み取り、変数置換を行い、解析されたテンプレートを何らかのフォルダーに出力する必要があります。それを行う最良の方法は何ですか?いくつかの方法が考えられます。どれが最良ですか、それを行うためのいくつかのより良い方法がありますか?私はそれを純粋なBashでやりたいです(例えばPHPで簡単です)
1) テキストファイルの$ {}プレースホルダーを置き換える方法
template.txt:
the number is ${i}
the Word is ${Word}
script.sh:
#!/bin/sh
#set variables
i=1
Word="dog"
#read in template one line at the time, and replace variables
#(more natural (and efficient) way, thanks to Jonathan Leffler)
while read line
do
eval echo "$line"
done < "./template.txt"
ところで、ここで外部ファイルに出力をリダイレクトするにはどうすればよいですか?変数に引用符が含まれている場合、何かをエスケープする必要がありますか?
2)cat&sedを使用して、各変数をその値に置き換えます。
与えられたtemplate.txt:
The number is ${i}
The Word is ${Word}
コマンド:
cat template.txt | sed -e "s/\${i}/1/" | sed -e "s/\${Word}/dog/"
多くの異なるシンボルをエスケープする必要があるため、私には悪いようであり、多くの変数を使用すると、行が長くなります。
他のエレガントで安全なソリューションを考えていただけますか?
これを使用できます:
Perl -p -i -e 's/\$\{([^}]+)\}/defined $ENV{$1} ? $ENV{$1} : $&/eg' < template.txt
すべての${...}
文字列を対応する環境変数に置き換えます(このスクリプトを実行する前にそれらをエクスポートすることを忘れないでください)。
純粋なbashの場合、これは機能するはずです(変数に$ {...}文字列が含まれていないと仮定します):
#!/bin/bash
while read -r line ; do
while [[ "$line" =~ (\$\{[a-zA-Z_][a-zA-Z_0-9]*\}) ]] ; do
LHS=${BASH_REMATCH[1]}
RHS="$(eval echo "\"$LHS\"")"
line=${line//$LHS/$RHS}
done
echo "$line"
done
。 RHSがそれ自体を参照する変数を参照してもハングしないソリューション:
#!/ bin/bash line = "$(cat; echo -na)" end_offset = $ {#line} while [["$ {line:0:$ end_offset} "=〜(。*)(\ $\{([a-zA-Z _] [a-zA-Z_0-9] *)\})(。*)]]; do PRE = "$ {BASH_REMATCH [1]}" POST = "$ {BASH_REMATCH [4]} $ {line:$ end_offset:$ {#line}}" VARNAME = "$ {BASH_REMATCH [3]}" eval 'VARVAL = "$' $ VARNAME '"' line = "$ PRE $ VARVAL $ POST" end_offset = $ {#PRE} done echo -n "$ {line:0:-1}"
WARNING:bashのNULを使用して入力を正しく処理する方法や、末尾の改行の量を保持する方法がわかりません。最後のバリアントは、シェルがバイナリ入力を「愛する」ため、そのまま表示されます。
read
はバックスラッシュを解釈します。read -r
はバックスラッシュを解釈しませんが、改行で終わらない場合は最後の行を削除します。"$(…)"
は、存在する限り多くの末尾の改行を削除するので、…
を; echo -n a
で終了し、echo -n "${line:0:-1}"
を使用します。これにより、最後の文字(a
)そして、入力にあったのと同じ数の末尾の改行を保持します(noを含む)。envsubst
を試してください
FOO=foo
BAR=bar
export FOO BAR
envsubst <<EOF
FOO is $FOO
BAR is $BAR
EOF
envsubstは私にとって初めてでした。素晴らしい。
記録のために、heredocを使用すると、confファイルをテンプレート化するのに最適な方法です。
STATUS_URI="/hows-it-goin"; MONITOR_IP="10.10.2.15";
cat >/etc/Apache2/conf.d/mod_status.conf <<EOF
<Location ${STATUS_URI}>
SetHandler server-status
Order deny,allow
Deny from all
Allow from ${MONITOR_IP}
</Location>
EOF
Sedの使用に同意します。これは、検索/置換に最適なツールです。私のアプローチは次のとおりです。
$ cat template.txt
the number is ${i}
the dog's name is ${name}
$ cat replace.sed
s/${i}/5/
s/${name}/Fido/
$ sed -f replace.sed template.txt > out.txt
$ cat out.txt
the number is 5
the dog's name is Fido
Evalは本当にうまく機能すると思います。改行、空白、あらゆる種類のbashを含むテンプレートを処理します。もちろん、テンプレート自体を完全に制御できる場合:
$ cat template.txt
variable1 = ${variable1}
variable2 = $variable2
my-ip = \"$(curl -s ifconfig.me)\"
$ echo $variable1
AAA
$ echo $variable2
BBB
$ eval "echo \"$(<template.txt)\"" 2> /dev/null
variable1 = AAA
variable2 = BBB
my-ip = "11.22.33.44"
Evalは任意のコードを実行できるため、このメソッドはもちろん慎重に使用する必要があります。これをrootとして実行することはほとんど問題外です。テンプレート内の引用符はエスケープする必要があります。エスケープしないと、eval
によって引用されます。
cat
よりもecho
を好む場合は、ここでドキュメントを使用することもできます
$ eval "cat <<< \"$(<template.txt)\"" 2> /dev/null
@plockcは、bash quoteのエスケープの問題を回避するソリューションを提案しました。
$ eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null
編集: Sudoを使用してこれをrootとして実行することに関する部分を削除しました...
編集:引用符をエスケープする方法についてのコメントを追加し、plockcのソリューションをミックスに追加しました!
Mogsieのようなbashソリューションがありますが、二重引用符のエスケープを回避できるように、herestringの代わりにheredocを使用します
eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null
構成ファイルに二重引用符を保持する必要があったため、sedで二重引用符を二重にエスケープすると次のようになります。
render_template() {
eval "echo \"$(sed 's/\"/\\\\"/g' $1)\""
}
末尾の改行を保持することは考えられませんが、間にある空の行は保持されます。
それは古いトピックですが、IMOはここでよりエレガントなソリューションを見つけました: http://pempek.net/articles/2013/07/08/bash-sh-as-template-engine/
#!/bin/sh
# render a template configuration file
# expand variables + preserve formatting
render_template() {
eval "echo \"$(cat $1)\""
}
user="Gregory"
render_template /path/to/template.txt > path/to/configuration_file
GrégoryPakosz へのすべてのクレジット。
受け入れられた答えのより長いがより堅牢なバージョン:
Perl -pe 's;(\\*)(\$([a-zA-Z_][a-zA-Z_0-9]*)|\$\{([a-zA-Z_][a-zA-Z_0-9]*)\})?;substr($1,0,int(length($1)/2)).($2&&length($1)%2?$2:$ENV{$3||$4});eg' template.txt
これにより、$VAR
のすべてのインスタンスが展開されますor${VAR}
は、環境値(または、未定義の場合は空文字列)。
バックスラッシュを適切にエスケープし、置換を禁止するためにバックスラッシュでエスケープされた$を受け入れます(envsubstとは異なり、はこれを行いません)。
したがって、環境が次の場合:
FOO=bar
BAZ=kenny
TARGET=backslashes
NOPE=engi
あなたのテンプレートは:
Two ${TARGET} walk into a \\$FOO. \\\\
\\\$FOO says, "Delete C:\\Windows\\System32, it's a virus."
$BAZ replies, "\${NOPE}s."
結果は次のようになります。
Two backslashes walk into a \bar. \\
\$FOO says, "Delete C:\Windows\System32, it's a virus."
kenny replies, "${NOPE}s."
$の前のバックスラッシュのみをエスケープする場合(テンプレートに「C:\ Windows\System32」を変更せずに記述することができます)、このわずかに変更したバージョンを使用します。
Perl -pe 's;(\\*)(\$([a-zA-Z_][a-zA-Z_0-9]*)|\$\{([a-zA-Z_][a-zA-Z_0-9]*)\});substr($1,0,int(length($1)/2)).(length($1)%2?$2:$ENV{$3||$4});eg' template.txt
私はこのようにして、おそらく効率は悪くなりますが、読みやすく、保守しやすいでしょう。
TEMPLATE='/path/to/template.file'
OUTPUT='/path/to/output.file'
while read LINE; do
echo $LINE |
sed 's/VARONE/NEWVALA/g' |
sed 's/VARTWO/NEWVALB/g' |
sed 's/VARTHR/NEWVALC/g' >> $OUTPUT
done < $TEMPLATE
envsubstを使って車輪を再発明する代わりに、環境変数から構成ファイルを構築するなど、ほとんどすべてのシナリオで使用できますドッカーコンテナ。
Macで homebrew になっていることを確認したら、gettextからリンクします。
brew install gettext
brew link --force gettext
./template.cfg
# We put env variables into placeholders here
this_variable_1 = ${SOME_VARIABLE_1}
this_variable_2 = ${SOME_VARIABLE_2}
./.env:
SOME_VARIABLE_1=value_1
SOME_VARIABLE_2=value_2
./configure.sh
#!/bin/bash
cat template.cfg | envsubst > whatever.cfg
今すぐ使用してください:
# make script executable
chmod +x ./configure.sh
# source your variables
. .env
# export your variables
# In practice you may not have to manually export variables
# if your solution depends on tools that utilise .env file
# automatically like pipenv etc.
export SOME_VARIABLE_1 SOME_VARIABLE_2
# Create your config file
./configure.sh
純粋なbashを使用してZyXから答えを取得しますが、新しいスタイルの正規表現マッチングと間接的なパラメーター置換を使用すると、次のようになります。
#!/bin/bash
regex='\$\{([a-zA-Z_][a-zA-Z_0-9]*)\}'
while read line; do
while [[ "$line" =~ $regex ]]; do
param="${BASH_REMATCH[1]}"
line=${line//${BASH_REMATCH[0]}/${!param}}
done
echo $line
done
別の純粋なbashソリューションを次に示します。
$ cat code
#!/bin/bash
LISTING=$( ls )
cat_template() {
echo "cat << EOT"
cat "$1"
echo EOT
}
cat_template template | LISTING="$LISTING" bash
$ cat template
(末尾の改行と二重引用符付き)
<html>
<head>
</head>
<body>
<p>"directory listing"
<pre>
$( echo "$LISTING" | sed 's/^/ /' )
<pre>
</p>
</body>
</html>
出力
<html>
<head>
</head>
<body>
<p>"directory listing"
<pre>
code
template
<pre>
</p>
</body>
</html>
Perlを使用するオプションがあり、environment変数のみ(allShell変数)、検討 スチュアートP.ベントレーの堅牢な答え.
この答えは、bash-only solutionを提供することを目指しています-eval
の使用にもかかわらず-safe to useであるべきです。
goalsは次のとおりです。
${name}
および$name
変数参照の両方の展開をサポートします。$(...)
およびレガシー構文`...`
)$((...))
およびレガシー構文$[...]
)。\
(\${name}
)を前に付けることにより、変数展開の選択的な抑制を許可します。"
および\
インスタンス。関数expandVars()
:
expandVars() {
local txtToEval=$* txtToEvalEscaped
# If no arguments were passed, process stdin input.
(( $# == 0 )) && IFS= read -r -d '' txtToEval
# Disable command substitutions and arithmetic expansions to prevent execution
# of arbitrary commands.
# Note that selectively allowing $((...)) or $[...] to enable arithmetic
# expressions is NOT safe, because command substitutions could be embedded in them.
# If you fully trust or control the input, you can remove the `tr` calls below
IFS= read -r -d '' txtToEvalEscaped < <(printf %s "$txtToEval" | tr '`([' '\1\2\3')
# Pass the string to `eval`, escaping embedded double quotes first.
# `printf %s` ensures that the string is printed without interpretation
# (after processing by by bash).
# The `tr` command reconverts the previously escaped chars. back to their
# literal original.
eval printf %s "\"${txtToEvalEscaped//\"/\\\"}\"" | tr '\1\2\3' '`(['
}
例:
$ expandVars '\$HOME="$HOME"; `date` and $(ls)'
$HOME="/home/jdoe"; `date` and $(ls) # only $HOME was expanded
$ printf '\$Shell=${Shell}, but "$(( 1 \ 2 ))" will not expand' | expandVars
$Shell=/bin/bash, but "$(( 1 \ 2 ))" will not expand # only ${Shell} was expanded
${HOME:0:$(echo 10)}
などの埋め込みコマンドまたは算術置換が含まれていない限り、${HOME:0:10}
などのnon-basic変数展開もサポートします。$(
および`
インスタンスが盲目的にエスケープされるため)。${HOME
(閉じる}
の欠落)などの不正な形式の変数参照により、関数が破損します。\$name
は展開を防ぎます。\
が後に続かない単一の$
はそのまま保持されます。\
インスタンスを表現する場合は、それらをdoubleする必要があります。例えば。:\\
-> \
-\
と同じ\\\\
-> \\
0x1
、0x2
、0x3
。eval
を使用しないソリューションについては以下を参照してください。onlyが${name}
拡張をサポートする、より制限的なソリューションを探している場合-つまり、mandatory$name
参照を無視する中括弧- この答え を参照.
bash-onlyの改良バージョン、eval
-- 受け入れられた回答からの無料ソリューションです:
改善点は次のとおりです。
${name}
および$name
変数参照の両方の展開のサポート。\
-エスケープ変数参照のサポート。eval
ベースのソリューションとは異なり、 IFS= read -d '' -r lines # read all input from stdin at once
end_offset=${#lines}
while [[ "${lines:0:end_offset}" =~ (.*)\$(\{([a-zA-Z_][a-zA-Z_0-9]*)\}|([a-zA-Z_][a-zA-Z_0-9]*))(.*) ]] ; do
pre=${BASH_REMATCH[1]} # everything before the var. reference
post=${BASH_REMATCH[5]}${lines:end_offset} # everything after
# extract the var. name; it's in the 3rd capture group, if the name is enclosed in {...}, and the 4th otherwise
[[ -n ${BASH_REMATCH[3]} ]] && varName=${BASH_REMATCH[3]} || varName=${BASH_REMATCH[4]}
# Is the var ref. escaped, i.e., prefixed with an odd number of backslashes?
if [[ $pre =~ \\+$ ]] && (( ${#BASH_REMATCH} % 2 )); then
: # no change to $lines, leave escaped var. ref. untouched
else # replace the variable reference with the variable's value using indirect expansion
lines=${pre}${!varName}${post}
fi
end_offset=${#pre}
done
printf %s "$lines"
別のソリューションを次に示します。すべての変数とテンプレートファイルの内容を含むbashスクリプトを生成します。このスクリプトは次のようになります。
Word=dog
i=1
cat << EOF
the number is ${i}
the Word is ${Word}
EOF
このスクリプトをbashに渡すと、目的の出力が生成されます。
the number is 1
the Word is dog
次に、そのスクリプトを生成し、そのスクリプトをbashにフィードする方法を示します。
(
# Variables
echo Word=dog
echo i=1
# add the template
echo "cat << EOF"
cat template.txt
echo EOF
) | bash
cat
コマンドを生成しますこの出力をファイルにリダイレクトする場合は、最後の行を次のように置き換えます。
) | bash > output.txt
このページでは、 awkでの回答 について説明します
awk '{while(match($0,"[$]{[^}]*}")) {var=substr($0,RSTART+2,RLENGTH -3);gsub("[$]{"var"}",ENVIRON[var])}}1' < input.txt > output.txt
shtpl の場合に最適です。 (私のプロジェクトですので、あまり使用されておらず、ドキュメントもありません。しかし、とにかく、それが提供するソリューションがあります。テストしてください。)
実行するだけです:
$ i=1 Word=dog sh -c "$( shtpl template.txt )"
結果は次のとおりです。
the number is 1
the Word is dog
楽しんで。
# Usage: template your_file.conf.template > your_file.conf
template() {
local IFS line
while IFS=$'\n\r' read -r line ; do
line=${line//\\/\\\\} # escape backslashes
line=${line//\"/\\\"} # escape "
line=${line//\`/\\\`} # escape `
line=${line//\$/\\\$} # escape $
line=${line//\\\${/\${} # de-escape ${ - allows variable substitution: ${var} ${var:-default_value} etc
# to allow arithmetic expansion or command substitution uncomment one of following lines:
# line=${line//\\\$\(/\$\(} # de-escape $( and $(( - allows $(( 1 + 2 )) or $( command ) - UNSECURE
# line=${line//\\\$\(\(/\$\(\(} # de-escape $(( - allows $(( 1 + 2 ))
eval "echo \"${line}\"";
done < "$1"
}
これは、好みに合わせて調整可能な純粋なbash関数であり、本番環境で使用され、どの入力でも壊れないはずです。それが壊れた場合-私に知らせてください。
また、bashibleを使用することもできます(内部的には上記または下記の評価アプローチを使用します)。
複数の部分からHTMLを生成する方法の例があります。
https://github.com/mig1984/bashible/tree/master/examples/templates
他のいくつかの回答に基づいて変更されたPerl
スクリプトは次のとおりです。
Perl -pe 's/([^\\]|^)\$\{([a-zA-Z_][a-zA-Z_0-9]*)\}/$1.$ENV{$2}/eg' -i template
機能(私のニーズに基づいていますが、簡単に変更できるはずです):
ここで単純な変数置換pythonスクリプトを見てください: https://github.com/jeckep/vsubst
使い方はとても簡単です:
python subst.py --props secure.properties --src_path ./templates --dst_path ./dist
空白を保持するbash関数は次のとおりです。
# Render a file in bash, i.e. expand environment variables. Preserves whitespace.
function render_file () {
while IFS='' read line; do
eval echo \""${line}"\"
done < "${1}"
}