如何在文本文件中replace$ {}占位符?
我想把一个“模板”文件的输出传送到MySQL,这个文件有一些像${dbName}
这样的variables。 什么是命令行实用程序来replace这些实例并将输出转储到标准输出?
Sed !
鉴于template.txt:
数字是$ {i} 这个词是$ {word}
我们只能说:
sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.txt
感谢Jonathan Leffler的提示,将多个-e
parameter passing给相同的sed
调用。
更新
这是yottatsa在类似问题上的一个解决scheme,只能replace$ VAR或$ {VAR}这样的variables,并且是一个简单的单行
i=32 word=foo envsubst < template.txt
当然,如果我和你的文字都在你的环境中,那么这是正义的
envsubst < template.txt
在我的Mac上,它看起来像是作为gettext和MacGPG2的一部分安装的
老答案
这是一个类似的问题从mogsie的解决scheme的改进,我的解决scheme并不需要你双引号,莫格西的,但他是一个class轮!
eval "cat <<EOF $(<template.txt) EOF " 2> /dev/null
这两个解决scheme的强大之处在于,您只能得到几种不会正常发生$((…)),`…`和$(…)的shell扩展types,尽pipe反斜杠是转义字符在这里,但你不必担心parsing有一个错误,它可以做很多行。
使用/bin/sh
。 创build一个设置variables的小shell脚本,然后使用shell本身parsing模板。 像这样(编辑来正确处理换行符):
档案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的macros处理器。 所以,而不是我最初指定的variables,你会使用:
$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
这里是我的解决scheme与基于前答案的Perl,取代环境variables:
perl -p -e 's/\$\{(\w+)\}/(exists $ENV{$1}?$ENV{$1}:"missing variable $1")/eg' < infile > outfile
如果你打算使用Perl ,那将是我的build议。 尽pipe可能有一些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
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
这是一个强大的Bash函数 – 尽pipe使用eval
– 应该是安全的使用。
input文本中的所有${varName}
variables引用都根据调用shell的variables进行扩展。
没有其他扩展名:既没有包含在{...}
variables引用(例如$varName
),也没有命令replace( $(...)
和遗留语法`...`
),算术replace( $((...))
和传统语法$[...]
)。
把$
看作一个字面意思, 例如: \${HOME}
请注意,input只能通过stdin接受。
例:
$ expandVarsStrict <<<'$HOME is "${HOME}"; `date` and \$(ls)' # only ${HOME} is expanded $HOME is "/Users/jdoe"; `date` and $(ls)
function源代码:
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 }
该函数假定input中不存在0x3
和0x4
控制字符,因为这些字符。 在内部使用 – 因为函数处理文本 ,这应该是一个安全的假设。
我会build议使用像Sigil : https : //github.com/gliderlabs/sigil
它被编译为一个单一的二进制文件,因此在系统上安装非常简单。
那么你可以做一个简单的一行,如下所示:
cat my-file.conf.template | sigil -p $(env) > my-file.conf
这比eval
更安全,然后使用正则expression式或sed
更容易
创buildrendertemplate.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
我发现这个线程,同时想知道同样的事情。 它启发了我(这反过来小心)
$ echo $MYTEST pass! $ cat FILE hello $MYTEST world $ eval echo `cat FILE` hello pass! world
这里有很多的select,但是我觉得我会把它扔在堆上。 它是基于perl的,只能以$ {…}的forms作为目标variables,将文件作为参数处理,并在stdout上输出转换后的文件:
use Env; Env::import(); while(<>) { $_ =~ s/(\${\w+})/$1/eeg; $text .= $_; } print "$text";
当然,我不是一个真正的perl人,所以可能会有一个致命的缺陷(虽然适用于我)。
如果你有configuration文件格式的控制,它可以在bash中完成。 你只需要发送(“。”)configuration文件而不是子shell。 这可以确保variables是在当前shell(并继续存在)的上下文中创build的,而不是子shell(在子shell退出时该variables消失)。
$ 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
如果你的configuration文件不能是一个shell脚本,你可以在执行之前“编译”它(编译取决于你的input格式)。
$ 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,并且希望你不会破坏你的数据库:-)。