回答:
セド!
与えられたtemplate.txt:
数は$ {i}です 単語は$ {word}です
私たちはただ言う必要があります:
sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.txt
複数の-e
引数を同じsed
呼び出しに渡すヒントを提供してくれたJonathan Lefflerに感謝します。
cat
。必要なのはsed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.text
。
sed
エスケープされたテキストが必要ですが、これは面倒です。
$ VARや$ {VAR}などの変数の置換のみを行う同様の質問に対するyottatsaの解決策は次のとおりです。簡単なワンライナーです。
i=32 word=foo envsubst < template.txt
もちろん、私と言葉があなたの環境にあれば、それはちょうどです
envsubst < template.txt
私のMacでは、gettextの一部として、またMacGPG2からインストールされたようです。
これは、同様の質問に対するmogsieからのソリューションの改善です。私のソリューションでは、二重引用符をescaleする必要はありませんが、mogsieはそうですが、彼は1つのライナーです!
eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null
これら二つの解決策の電源はバックスラッシュがにもかかわらずあなただけ、(...)が発生していないシェル拡張のいくつかのタイプを、通常$((...))、 `...`取得し、$ということですここではエスケープ文字を使用していますが、解析にバグがあることを心配する必要はありません。また、複数行を正常に実行します。
envsubst
envarsがエクスポートされていないと、ベアが機能しないことがわかりました。
envsubst
は、その名前が示すように、環境変数のみを認識し、シェル変数は認識しません。これenvsubst
はGNUユーティリティであるため、プリインストールされていないか、すべてのプラットフォームで利用できるわけではないことにも注意してください。
を使用し/bin/sh
ます。変数を設定する小さなシェルスクリプトを作成し、シェル自体を使用してテンプレートを解析します。そのように(改行を正しく処理するように編集):
the number is ${i}
the word is ${word}
#!/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"
#sh script.sh
the number is 1
the word is dog
bash
入力内のすべてのコマンドが実行されます。テンプレートが「words is; rm -rf $ HOME」の場合、ファイルが失われます。
read
記述されているように、コマンドは各行から先頭と末尾の空白を削除し、文字を「食べます」\
、(c)これを完全に使用する場合にのみ使用します入力に埋め込まれたコマンド置換(`…`
または$(…)
)により、の使用により任意のコマンドを実行できるため、入力を信頼または制御しますeval
。最後に、echo
コマンドラインオプションの1つと、行の先頭を間違える可能性が少しあります。
私は最近の関心を考慮して、もう一度これについて考えていました。私が最初に考えていたツールはm4
、autotoolsのマクロプロセッサだったと思います。したがって、最初に指定した変数の代わりに、次を使用します。
$echo 'I am a DBNAME' | m4 -DDBNAME="database name"
envsubst
他の回答で述べたように、私はこの単純な変数置換/テンプレートの使用法に使用します。m4
は優れたツールですが、機能が充実した本格的なプリプロセッサであり、単純にいくつかの変数を置き換えたいだけの場合は、複雑さは必要ありません。
template.txt
Variable 1 value: ${var1}
Variable 2 value: ${var2}
data.sh
#!/usr/bin/env bash
declare var1="value 1"
declare var2="value 2"
parser.sh
#!/usr/bin/env bash
# args
declare file_data=$1
declare file_input=$2
declare file_output=$3
source $file_data
eval "echo \"$(< $file_input)\"" > $file_output
./parser.sh data.sh template.txt parsed_file.txt
parsed_file.txt
Variable 1 value: value 1
Variable 2 value: value 2
`…`
または$(…)
)はeval
、の使用による任意のコマンドの実行、およびの使用によるシェルコードの直接実行を許可するためですsource
。また、入力内の二重引用符は静かに破棄され、echo
行の先頭をコマンドラインオプションの1つと間違える可能性があります。
以下は、使用しても安全に使用できる堅牢なBash関数ですeval
。
${varName}
入力テキスト内のすべての変数参照は、呼び出し側シェルの変数に基づいて展開されます。
他には何もないは展開されません。名前が囲まれていない変数参照{...}
(など$varName
)も、コマンド置換($(...)
およびレガシー構文`...`
)も、算術置換($((...))
およびレガシー構文$[...]
)もありません。
$
aをリテラルとして扱うには、\
それをエスケープします。例えば:\${HOME}
入力はstdin経由でのみ受け入れられることに注意してください。
例:
$ expandVarsStrict <<<'$HOME is "${HOME}"; `date` and \$(ls)' # only ${HOME} is expanded
$HOME is "/Users/jdoe"; `date` and $(ls)
関数のソースコード:
expandVarsStrict(){
local line lineEscaped
while IFS= read -r line || [[ -n $line ]]; do # the `||` clause ensures that the last line is read even if it doesn't end with \n
# Escape ALL chars. that could trigger an expansion..
IFS= read -r -d '' lineEscaped < <(printf %s "$line" | tr '`([$' '\1\2\3\4')
# ... then selectively reenable ${ references
lineEscaped=${lineEscaped//$'\4'{/\${}
# Finally, escape embedded double quotes to preserve them.
lineEscaped=${lineEscaped//\"/\\\"}
eval "printf '%s\n' \"$lineEscaped\"" | tr '\1\2\3\4' '`([$'
done
}
関数はという前提0x1
、0x2
、0x3
、および0x4
制御文字を、それらの文字から、入力に存在します。内部で使用されます -関数はテキストを処理するため、これは安全な前提です。
eval
てもかなり安全です。
"
適切にエスケープします!)
${FOO:-bar}
たり、設定されている場合にのみ出力したりできることです${HOME+Home is ${HOME}}
。小さな拡張機能を使用すると、不足している変数の終了コードも返される可能性があります${FOO?Foo is missing}
が、現時点では tldp.org/LDP/abs/html/parameter-substitution.htmlにこれらのリストが表示されていれば助かります
作成rendertemplate.sh
:
#!/usr/bin/env bash
eval "echo \"$(cat $1)\""
そしてtemplate.tmpl
:
Hello, ${WORLD}
Goodbye, ${CHEESE}
テンプレートをレンダリングします。
$ export WORLD=Foo
$ CHEESE=Bar ./rendertemplate.sh template.tmpl
Hello, Foo
Goodbye, Bar
$(rm -rf ~)
、それをコードとして実行しています。
eval "echo \"$(cat $1)\""
よく働く !
以前の答えに基づいたperlを使用した私の解決策は次のとおりです。環境変数を置き換えます。
perl -p -e 's/\$\{(\w+)\}/(exists $ENV{$1}?$ENV{$1}:"missing variable $1")/eg' < infile > outfile
Perlを使用することにオープンであれば、それが私の提案です。おそらくこれをもっと簡単に行う方法を知っているsedやAWKのエキスパートがおそらくいるでしょう。置換用のdbName以外のより複雑なマッピングがある場合、これはかなり簡単に拡張できますが、その時点で標準のPerlスクリプトに追加することもできます。
perl -p -e 's/\$\{dbName\}/testdb/s' yourfile | mysql
少し複雑な処理を行う短いPerlスクリプト(複数のキーを処理する):
#!/usr/bin/env perl
my %replace = ( 'dbName' => 'testdb', 'somethingElse' => 'fooBar' );
undef $/;
my $buf = <STDIN>;
$buf =~ s/\$\{$_\}/$replace{$_}/g for keys %replace;
print $buf;
上記のスクリプトにreplace-scriptという名前を付けると、次のように使用できます。
replace-script < yourfile | mysql
ファイルの内容が二重引用符の間に入力されたかのように、シェルに置換を行わせる方法は次のとおりです。
内容を含むtemplate.txtの例を使用:
The number is ${i}
The word is ${word}
次の行により、シェルはtemplate.txtの内容を補間し、結果を標準出力に書き込みます。
i='1' word='dog' sh -c 'echo "'"$(cat template.txt)"'"'
説明:
i
そして、word
環境変数がの実行にscoppedとして渡されますsh
。sh
渡された文字列の内容を実行します。echo "
' + " $(cat template.txt)
" + ' "
'"
で置換が行われるため、「$(cat template.txt)
」の出力になりcat template.txt
ます。sh -c
なります。
echo "The number is ${i}\nThe word is ${word}"
、i
、およびword
は、指定された環境変数です。'$(rm -rf ~)'$(rm -rf ~)
、テンプレートファイルのリテラルの引用符は、展開前に追加した引用符と一致します。
'$(echo a)'$(echo a)
です。生産し'a'a
ます。発生している主なことは、最初echo a
の'
が評価されていることです。これはにあるため、期待どおりではないかもしれませんが、引用文字列に'
含めるのと同じ動作です。'
"
"
引用符で囲まれた文字列(を含む$(...)
)を展開することがポイントです。
${varname}
ていない他、高セキュリティリスクの拡大、。
echo "
、その後に二重引用符付きの文字列がtemplate.txt
続く文字列が続き、さらに別のリテラル文字列が続き"
、すべてがに渡される単一の引数に連結されsh -c
ます。'
一致させることはできませんが(内側のシェルに渡されるのではなく、外側のシェルによって消費されるため)、"
確かに一致するため、テンプレートを含むテンプレートをGotcha"; rm -rf ~; echo "
実行できます。
file.tpl:
The following bash function should only replace ${var1} syntax and ignore
other shell special chars such as `backticks` or $var2 or "double quotes".
If I have missed anything - let me know.
script.sh:
template(){
# usage: template file.tpl
while read -r line ; do
line=${line//\"/\\\"}
line=${line//\`/\\\`}
line=${line//\$/\\\$}
line=${line//\\\${/\${}
eval "echo \"$line\"";
done < ${1}
}
var1="*replaced*"
var2="*not replaced*"
template file.tpl > result.txt
\$(date)
while IFS= read -r line; do
として、read
コマンドとして使用することをお勧めします。そうしないと、各入力行から先頭と末尾の空白が削除されます。また、echo
行の先頭をコマンドラインオプションの1つと間違える可能性があるため、を使用することをお勧めしますprintf '%s\n'
。最後に、二重引用符を使用する方が安全${1}
です。
私はSigilのようなものを使用することをお勧めします:https : //github.com/gliderlabs/sigil
単一のバイナリにコンパイルされるため、システムへのインストールは非常に簡単です。
その後、次のような単純なワンライナーを実行できます。
cat my-file.conf.template | sigil -p $(env) > my-file.conf
これはeval
正規表現を使用するよりもはるかに安全で簡単ですsed
cat
して使用<my-file.conf.template
することをお勧めしますsigil
。
同じことを考えながらこのスレッドを見つけました。それは私にインスピレーションを与えました(バッククォートに注意)
$ echo $MYTEST
pass!
$ cat FILE
hello $MYTEST world
$ eval echo `cat FILE`
hello pass! world
$(cat file)
は$(< file)
eval echo "\"$(cat FILE)\""
がありますが、それでも入力内の二重引用符が破棄されるので不十分な場合があります。
`…`
または$(…)
)により、eval
。
ここにはたくさんの選択肢がありますが、私は鉱山を山積みにすると思いました。これはperlベースで、$ {...}形式の変数のみを対象とし、処理するファイルを引数として取り、変換されたファイルをstdoutに出力します。
use Env;
Env::import();
while(<>) { $_ =~ s/(\${\w+})/$1/eeg; $text .= $_; }
print "$text";
もちろん、私は実際にはperlの人ではないので、致命的な欠陥が簡単に発生する可能性があります(私にとってはうまくいきます)。
Env::import();
行をドロップすることができます-インポートは暗示されていuse
ます。また、最初にメモリ内の出力全体を構築しないことをお勧めします。ループprint;
の$text .= $_;
内部ではなく単に使用し、ポストループprint
コマンドを削除します。
構成ファイルの形式を制御できる場合は、bash自体で実行できます。構成ファイルをサブシェルするのではなく、ソース( "。")にするだけです。これにより、サブシェル(変数がサブシェルの終了時に消える場所)ではなく、現在のシェル(および存在し続ける)のコンテキストで変数が作成されます。
$ cat config.data
export parm_jdbc=jdbc:db2://box7.co.uk:5000/INSTA
export parm_user=pax
export parm_pwd=never_you_mind
$ cat go.bash
. config.data
echo "JDBC string is " $parm_jdbc
echo "Username is " $parm_user
echo "Password is " $parm_pwd
$ bash go.bash
JDBC string is jdbc:db2://box7.co.uk:5000/INSTA
Username is pax
Password is never_you_mind
設定ファイルがシェルスクリプトにできない場合は、実行する前にファイルを「コンパイル」するだけです(コンパイルは入力フォーマットによって異なります)。
$ cat config.data
parm_jdbc=jdbc:db2://box7.co.uk:5000/INSTA # JDBC URL
parm_user=pax # user name
parm_pwd=never_you_mind # password
$ cat go.bash
cat config.data
| sed 's/#.*$//'
| sed 's/[ \t]*$//'
| sed 's/^[ \t]*//'
| grep -v '^$'
| sed 's/^/export '
>config.data-compiled
. config.data-compiled
echo "JDBC string is " $parm_jdbc
echo "Username is " $parm_user
echo "Password is " $parm_pwd
$ bash go.bash
JDBC string is jdbc:db2://box7.co.uk:5000/INSTA
Username is pax
Password is never_you_mind
特定のケースでは、次のようなものを使用できます。
$ cat config.data
export p_p1=val1
export p_p2=val2
$ cat go.bash
. ./config.data
echo "select * from dbtable where p1 = '$p_p1' and p2 like '$p_p2%' order by p1"
$ bash go.bash
select * from dbtable where p1 = 'val1' and p2 like 'val2%' order by p1
次に、go.bashの出力をMySQLとvoilaにパイプします。うまくいけば、データベースを破壊しないでください:-)。
go.bash
)でechoステートメントについて話している場合、スティックの終わりが間違っています-それらは解決策の一部ではなく、変数が存在していることを示すための単なる方法です正しく設定してください。