如何替换文本文件中的 ${} 占位符?
-
03-07-2019 - |
题
我想将“模板”文件的输出通过管道传输到 MySQL,该文件具有如下变量 ${dbName}
穿插。用于替换这些实例并将输出转储到标准输出的命令行实用程序是什么?
解决方案
Sed !
给出template.txt:
The number is ${i} The word is ${word}
我们只需说:
sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.txt
感谢Jonathan Leffler提示将多个 -e
参数传递给相同的 sed
调用。
其他提示
更新
以下是来自 yottatsa 的类似问题的解决方案,该问题仅替换$ VAR或$ {VAR等变量},是一个简短的单行
i=32 word=foo envsubst < template.txt
当然,如果你的环境中有 i 和 word ,那么它只是
envsubst < template.txt
在我的Mac上看起来它是作为 gettext 的一部分安装的,并且来自 MacGPG2
旧答案
以下是 mogsie 对类似问题的解决方案的改进,我的解决方案不要求你加倍扩展引用,mogsie's,但他是一个单行!
eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null
这两个解决方案的强大之处在于你只能得到几种类型的shell扩展,这些扩展通常不会出现$((...)),`...`和$(...),尽管反斜杠是这里的一个转义字符,但是您不必担心解析有错误,并且它可以很好地执行多行。
使用 / bin / sh
。创建一个设置变量的小shell脚本,然后使用shell本身解析模板。像这样(编辑以正确处理换行):
文件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
这是基于以前的答案的perl解决方案,替换环境变量:
perl -p -e 's/\$\{(\w+)\}/(exists $ENV{$1}?$ENV{$1}:"missing variable $1")/eg' < infile > outfile
这是一个 强大的 Bash 功能 那 - 尽管使用 eval
- 应该可以安全使用。
全部 ${varName}
输入文本中的变量引用根据调用 shell 的变量进行扩展。
没有其他的 被扩展:两个变量都没有引用其名称 不是 封闭在 {...}
(例如 $varName
),也不是命令替换($(...)
和遗留语法 `...`
),也不是算术替换 ($((...))
和遗留语法 $[...]
).
治疗一个 $
作为字面意思, \
-逃避它;例如。:\${HOME}
请注意,输入仅通过以下方式接受 标准输入.
例子:
$ 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
控制字符出现在输入中,因为这些字符。在内部使用 - 因为函数处理 文本, ,这应该是一个安全的假设。
如果您愿意使用 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/\$\{如果您愿意使用 Perl ,那将是我的建议。虽然可能有一些 sed 和/或 AWK 专家可能知道如何更容易地做到这一点。如果您有一个更复杂的映射,而不仅仅是dbName用于替换,那么您可以非常轻松地扩展它,但是您可以在此时将其放入标准的Perl脚本中。
perl -p -e 's/\$\{dbName\}/testdb/s' yourfile | mysql
一个简短的Perl脚本,可以做一些稍微复杂的事情(处理多个键):
replace-script < yourfile | mysql
如果您将上述脚本命名为replace-script,则可以按如下方式使用它:
<*>\}/$replace{如果您愿意使用 Perl ,那将是我的建议。虽然可能有一些 sed 和/或 AWK 专家可能知道如何更容易地做到这一点。如果您有一个更复杂的映射,而不仅仅是dbName用于替换,那么您可以非常轻松地扩展它,但是您可以在此时将其放入标准的Perl脚本中。
perl -p -e 's/\$\{dbName\}/testdb/s' yourfile | mysql
一个简短的Perl脚本,可以做一些稍微复杂的事情(处理多个键):
<*>
如果您将上述脚本命名为replace-script,则可以按如下方式使用它:
<*>}/g for keys %replace;
print $buf;
如果您将上述脚本命名为replace-script,则可以按如下方式使用它:
<*>创建 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
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
我建议使用 Sigil 之类的内容: https://github.com/gliderlabs/sigil
它被编译为单个二进制文件,因此在系统上安装非常容易。
然后你可以做一个简单的单行,如下所示:
cat my-file.conf.template | sigil -p $(env) > my-file.conf
这比 eval
更安全,然后使用正则表达式或 sed
我在想知道同样的事情时找到了这个帖子。它激发了我的灵感(小心反击)
$ echo $MYTEST
pass!
$ cat FILE
hello $MYTEST world
$ eval echo `cat FILE`
hello pass! world
这里有很多选择,但我认为我会把我扔到堆里。它是基于perl的,只定位$ {...}形式的变量,将文件作为参数处理并在stdout上输出转换后的文件:
use Env;
Env::import();
while(<>) { 这里有很多选择,但我认为我会把我扔到堆里。它是基于perl的,只定位$ {...}形式的变量,将文件作为参数处理并在stdout上输出转换后的文件:
<*>
当然我不是一个真正的perl人,所以很容易就会有一个致命的缺陷(虽然对我有用)。
=~ s/(\${\w+})/$1/eeg; $text .= 这里有很多选择,但我认为我会把我扔到堆里。它是基于perl的,只定位$ {...}形式的变量,将文件作为参数处理并在stdout上输出转换后的文件:
<*>
当然我不是一个真正的perl人,所以很容易就会有一个致命的缺陷(虽然对我有用)。
; }
print "$text";
当然我不是一个真正的perl人,所以很容易就会有一个致命的缺陷(虽然对我有用)。
如果您可以控制配置文件格式,则可以在bash中完成。您只需要配置(“。”)配置文件而不是子shell。这确保变量是在当前shell的上下文中创建的(并且继续存在)而不是子shell(当子shell退出时变量消失)。
$ 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
如果您的配置文件不能是shell脚本,您可以在执行之前“编译”它(编译取决于您的输入格式)。
$ 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 '^
在您的具体情况下,您可以使用以下内容:
$ 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中,瞧,希望你不会破坏你的数据库: - )。
| 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
在您的具体情况下,您可以使用以下内容:
<*>然后将go.bash的输出传输到MySQL中,瞧,希望你不会破坏你的数据库: - )。
这是一种让 shell 为您进行替换的方法,就好像文件的内容是在双引号之间键入的一样。
使用包含内容的 template.txt 示例:
The number is ${i}
The word is ${word}
以下行将导致 shell 插入 template.txt 的内容并将结果写入标准输出。
i='1' word='dog' sh -c 'echo "'"$(cat template.txt)"'"'
解释:
i
和word
作为环境变量传递给执行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
是指定的环境变量。