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

问题描述 投票:124回答:15

我想将一个“模板”文件的输出传递给MySQL,该文件包含散布的${dbName}等变量。什么是命令行实用程序来替换这些实例并将输出转储到标准输出?

bash command-line text-processing templating
15个回答
154
投票

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调用。


4
投票

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.是:

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

3
投票

我建议使用像Sigil这样的东西:https://github.com/gliderlabs/sigil

它被编译为单个二进制文件,因此在系统上安装非常容易。

然后你可以做一个简单的单行,如下所示:

cat my-file.conf.template | sigil -p $(env) > my-file.conf

这比eval更安全,然后使用正则表达式或sed更容易


2
投票

我发现这个帖子同时想知道同样的事情。它激发了我的灵感(小心反击)

$ echo $MYTEST
pass!
$ cat FILE
hello $MYTEST world
$ eval echo `cat FILE`
hello pass! world

2
投票

这里有很多选择,但我认为我会把我扔在堆里。它是基于perl的,只定位$ {...}形式的变量,将文件作为参数处理并在stdout上输出转换后的文件:

use Env;
Env::import();

while(<>) { $_ =~ s/(\${\w+})/$1/eeg; $text .= $_; }

print "$text";

当然我不是一个真正的perl人,所以很容易就会有一个致命的缺陷(虽然对我有用)。


2
投票

如果您可以控制配置文件格式,则可以在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 '^$'
        | 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,瞧,希望你不会破坏你的数据库:-)。


1
投票

这是一种让shell为你做替换的方法,好像文件的内容改为在双引号之间输入。

使用带有内容的template.txt示例:

The number is ${i}
The word is ${word}

以下行将导致shell插入template.txt的内容并将结果写入标准输出。

i='1' word='dog' sh -c 'echo "'"$(cat template.txt)"'"'

说明:

  • qazxsw poi和qazxsw poi作为环境变量传递给i的执行。
  • word执行传递的字符串的内容。
  • 彼此相邻的字符串成为一个字符串,该字符串是: 'sh'+“sh”+'echo "'
  • 由于替换是在$(cat template.txt)之间,“"”成为"的输出。
  • 所以$(cat template.txt)执行的命令变为: cat template.txt, 其中sh -cecho "The number is ${i}\nThe word is ${word}"是指定的环境变量。

140
投票

更新

以下是来自yottatsa的类似问题的解决方案,该问题仅替换$ VAR或$ {VAR}等变量,并且是一个简短的单行

i=32 word=foo envsubst < template.txt

当然,如果我和你的话语在你的环境中,那么它就是

envsubst < template.txt

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

老答案

这是对mogsie在类似问题上的解决方案的改进,我的解决方案并不要求你使用双引号,mogsie's,但他是一个单行!

eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null

这两个解决方案的优势在于你只能得到几种类型的shell扩展,这些扩展通常不会出现$((...)),`...`和$(...),尽管反斜杠是一个转义字符在这里,但你不必担心解析有一个错误,它做多行就好了。


41
投票

使用/bin/sh。创建一个设置变量的小shell脚本,然后使用shell本身解析模板。像这样(编辑以正确处理换行):

File template.txt:

the number is ${i}
the word is ${word}

File 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"

Output:

#sh script.sh
the number is 1
the word is dog

20
投票

鉴于最近的兴趣,我再次考虑这个问题,我认为我最初想到的工具是m4,它是autotools的宏处理器。因此,不是我最初指定的变量,而是使用:

$echo 'I am a DBNAME' | m4 -DDBNAME="database name"

13
投票

template.txt

Variable 1 value: ${var1}
Variable 2 value: ${var2}

data.是

#!/usr/bin/env bash
declare var1="value 1"
declare var2="value 2"

parser.是

#!/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

9
投票

这是我基于前一个答案的perl解决方案,替换环境变量:

perl -p -e 's/\$\{(\w+)\}/(exists $ENV{$1}?$ENV{$1}:"missing variable $1")/eg' < infile > outfile

9
投票

这是一个强大的Bash功能 - 尽管使用eval - 应该是安全的。

输入文本中的所有${varName}变量引用都基于调用shell的变量进行扩展。

没有其他东西被扩展:既没有名字未包含在{...}中的变量引用(例如$varName),也没有命令替换($(...)和遗留语法`...`),也没有算术替换($((...))和遗留语法$[...])。

$视为文字,\逃脱它; e.g.:\${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
}

该函数假定输入中不存在0x10x20x30x4控制字符,因为这些字符。在内部使用 - 因为函数处理文本,这应该是一个安全的假设。


7
投票

创建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

6
投票

如果您愿意使用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
© www.soinside.com 2019 - 2024. All rights reserved.