「テンプレート」ファイルの出力をMySQLにパイプします。このファイルには、${dbName}
散在するような変数があります。これらのインスタンスを置き換えて出力を標準出力にダンプするコマンドラインユーティリティとは何ですか?
18 に答える
セド!
与えられた template.txt:
番号は ${i} です 単語は ${word} です
私たちはただ言う必要があります:
sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.txt
-e
複数の引数を同じsed
呼び出しに渡すためのヒントをくれた Jonathan Leffler に感謝します。
アップデート
$VAR や ${VAR} などの変数の置換のみを行い、簡単なワンライナーである同様の質問に対するyottatsaの解決策を次に示します。
i=32 word=foo envsubst < template.txt
もちろん、iとwordがあなたの環境にある場合は、
envsubst < template.txt
私のMacでは、gettextの一部としてMacGPG2からインストールされたようです
古い回答
これは、同様の質問に対するmogsieからのソリューションの改善です。私のソリューションでは、二重引用符をエスケールする必要はありません.mogsieはそうしますが、彼はワンライナーです!
eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null
これら 2 つのソリューションの利点は、通常では発生しないシェル展開のいくつかのタイプしか得られないことです。$((...))、`...`、および $(... )。ここではエスケープ文字を使用しますが、解析にバグがあることを心配する必要はなく、複数行を問題なく実行できます。
を使用し/bin/sh
ます。変数を設定する小さなシェルスクリプトを作成し、シェル自体を使用してテンプレートを解析します。そのように(改行を正しく処理するように編集します):
ファイル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"
出力:
#sh script.sh
the number is 1
the word is dog
最近の興味から改めて考えてみたのですが、もともと考えていたツールはm4
autotools のマクロプロセッサだったと思います。したがって、最初に指定した変数の代わりに、次を使用します。
$echo 'I am a DBNAME' | m4 -DDBNAME="database name"
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
これは、使用しても安全に使用できる堅牢な Bash 関数です。eval
入力テキスト内のすべての${varName}
変数参照は、呼び出しシェルの変数に基づいて展開されます。
名前が囲まれていない変数参照{...}
( など$varName
)、コマンド置換 ($(...)
および 従来の構文`...`
)、算術置換 ($((...))
および 従来の構文) は展開されません$[...]
。
$
a をリテラルとして扱うには、 \
-escape it; 例えば:\${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
制御文字が入力に存在しないことを前提としています。は内部で使用されます - 関数はtextを処理するため、これは安全な仮定です。
以前の回答に基づいたperlを使用した私のソリューションは、環境変数を置き換えます:
perl -p -e 's/\$\{(\w+)\}/(exists $ENV{$1}?$ENV{$1}:"missing variable $1")/eg' < infile > outfile
Sigilのようなものを使用することをお勧めします: https://github.com/gliderlabs/sigil
単一のバイナリにコンパイルされるため、システムへのインストールは非常に簡単です。
次に、次のような単純なワンライナーを実行できます。
cat my-file.conf.template | sigil -p $(env) > my-file.conf
これはeval
、正規表現やsed
ファイルの内容が代わりに二重引用符で囲まれて入力されたかのように、シェルに置換を実行させる方法を次に示します。
コンテンツを含む template.txt の例を使用すると、次のようになります。
The number is ${i}
The word is ${word}
次の行により、シェルは template.txt の内容を補間し、結果を標準出力に書き込みます。
i='1' word='dog' sh -c 'echo "'"$(cat template.txt)"'"'
説明:
i
のword
実行にスコープされた環境変数として渡されますsh
。sh
渡された文字列の内容を実行します。- 隣り合わせに書かれた文字列は 1 つの文字列になります。その文字列は次のとおりです。
- '
echo "
' + "$(cat template.txt)
" + '"
'
- '
- の間で置換されているため
"
、"$(cat template.txt)
" は の出力となりますcat template.txt
。 - したがって、によって実行されるコマンドは次のように
sh -c
なります。echo "The number is ${i}\nThe word is ${word}"
、- とは
i
、word
指定された環境変数です。
もしあなたが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
ファイル.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
同じことを考えていたところ、このスレッドを見つけました。これは私にインスピレーションを与えました(バッククォートに注意してください)
$ echo $MYTEST
pass!
$ cat FILE
hello $MYTEST world
$ eval echo `cat FILE`
hello pass! world
ここにはたくさんの選択肢がありますが、私は自分のものを山に投げ入れることにしました。これは perl ベースで、${...} の形式の変数のみを対象とし、処理するファイルを引数として取り、変換されたファイルを stdout に出力します。
use Env;
Env::import();
while(<>) { $_ =~ s/(\${\w+})/$1/eeg; $text .= $_; }
print "$text";
もちろん、私は実際には perl の人ではないので、致命的な欠陥が簡単に存在する可能性があります (ただし、私には機能します)。
構成ファイル形式を制御できる場合は、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 と出来上がりにパイプします。データベースを破壊しないことを願っています :-)。
私にとってこれは最も簡単で強力なソリューションです。同じコマンドを使用して他のテンプレートを含めることもできますeval echo "$(<template.txt)
。
ネストされたテンプレートの例
- テンプレート ファイルを作成します。変数は通常の bash 構文
${VARIABLE_NAME}
または$VARIABLE_NAME
\
テンプレートで特殊文字をエスケープする必要があります。そうしないと、 によって解釈されeval
ます。
template.txt
Hello ${name}!
eval echo $(<nested-template.txt)
nested-template.txt
Nice to have you here ${name} :\)
- ソースファイルを作成する
template.source
declare name=royman
- テンプレートをパースする
source template.source && eval echo "$(<template.txt)"
- 出力
Hello royman!
Nice to have you here royman :)
潜在的に複数のファイルを perl で編集し、バックアップを行います。
perl -e 's/\$\{([^}]+)\}/defined $ENV{$1} ? $ENV{$1} : ""/eg' \
-i.orig \
-p config/test/*
という名前のシェル テンプレート スクリプトを作成しましたshtpl
。私shtpl
はジンジャのような構文を使用しています。これは、ansible を頻繁に使用するようになったので、よく知っています。
$ cat /tmp/test
{{ aux=4 }}
{{ myarray=( a b c d ) }}
{{ A_RANDOM=$RANDOM }}
$A_RANDOM
{% if $(( $A_RANDOM%2 )) == 0 %}
$A_RANDOM is even
{% else %}
$A_RANDOM is odd
{% endif %}
{% if $(( $A_RANDOM%2 )) == 0 %}
{% for n in 1 2 3 $aux %}
\$myarray[$((n-1))]: ${myarray[$((n-1))]}
/etc/passwd field #$n: $(grep $USER /etc/passwd | cut -d: -f$n)
{% endfor %}
{% else %}
{% for n in {1..4} %}
\$myarray[$((n-1))]: ${myarray[$((n-1))]}
/etc/group field #$n: $(grep ^$USER /etc/group | cut -d: -f$n)
{% endfor %}
{% endif %}
$ ./shtpl < /tmp/test
6535
6535 is odd
$myarray[0]: a
/etc/group field #1: myusername
$myarray[1]: b
/etc/group field #2: x
$myarray[2]: c
/etc/group field #3: 1001
$myarray[3]: d
/etc/group field #4:
私のgithubの詳細