如何在文本文件中replace$ {}占位符?

我想把一个“模板”文件的输出传送到MySQL,这个文件有一些像${dbName}这样的variables。 什么是命令行实用程序来replace这些实例并将输出转储到标准输出?

Sed !

鉴于template.txt:

数字是$ {i}
这个词是$ {word}

我们只能说:

 sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.txt 

感谢Jonathan Leffler的提示,将多个-eparameter passing给相同的sed调用。

更新

这是yottatsa在类似问题上的一个解决scheme,只能replace$ VAR或$ {VAR}这样的variables,并且是一个简单的单行

 i=32 word=foo envsubst < template.txt 

当然,如果和你的文字都在你的环境中,那么这是正义的

 envsubst < template.txt 

在我的Mac上,它看起来像是作为gettextMacGPG2的一部分安装的

老答案

这是一个类似的问题从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中不存在0x30x4控制字符,因为这些字符。 在内部使用 – 因为函数处理文本 ,这应该是一个安全的假设。

我会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,并且希望你不会破坏你的数据库:-)。