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

时间:2022-09-13 09:44:36

I want to pipe the output of a "template" file into MySQL, the file having variables like ${dbName} interspersed. What is the command line utility to replace these instances and dump the output to standard output?

我想把一个“模板”文件的输出输入到MySQL中,这个文件中有${dbName}这样的变量。替换这些实例并将输出转储到标准输出的命令行实用程序是什么?

14 个解决方案

#1


113  

Sed!

Sed !

Given template.txt:

鉴于template.txt:

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

we just have to say:

我们不得不说:

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

Thanks to Jonathan Leffler for the tip to pass multiple -e arguments to the same sed invocation.

感谢Jonathan Leffler的建议,将多个-e参数传递给同一个sed调用。

#2


100  

Update

Here is a solution from yottatsa on a similar question that only does replacement for variables like $VAR or ${VAR}, and is a brief one-liner

这里是yottatsa提出的一个类似问题的解决方案,它只替换变量$VAR或${VAR},并且是一个简短的一行程序。

i=32 word=foo envsubst < template.txt

Of course if i and word are in your environment, then it is just

当然,如果我和word在你的环境中,那么它就是公正的

envsubst < template.txt

On my Mac it looks like it was installed as part of gettext and from MacGPG2

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

Old Answer

Here is an improvement to the solution from mogsie on a similar question, my solution does not require you to escale double quotes, mogsie's does, but his is a one liner!

这是来自mogsie的一个类似问题的解决方案,我的解决方案不需要您使用escale双引号,mogsie的方法,但是他的是一个一行!

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

The power on these two solutions is that you only get a few types of shell expansions that don't occur normally $((...)), `...`, and $(...), though backslash is an escape character here, but you don't have to worry that the parsing has a bug, and it does multiple lines just fine.

这两种解决方案的强大之处在于,您只获得了少数几种通常不会出现的shell扩展。,还有$(…),虽然反斜杠在这里是一个转义字符,但是您不必担心解析有一个bug,而且它可以做很多行。

#3


41  

Use /bin/sh. Create a small shell script that sets the variables, and then parse the template using the shell itself. Like so (edit to handle newlines correctly):

使用/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

#4


17  

I was thinking about this again, given the recent interest, and I think that the tool that I was originally thinking of was m4, the macro processor for autotools. So instead of the variable I originally specified, you'd use:

考虑到最近的兴趣,我再次考虑这个问题,我认为我最初考虑的工具是m4,自动工具的宏处理器。所以不是我原先指定的变量,你可以用:

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

#5


11  

template.txt

template.txt

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

data.sh

data.sh

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

parser.sh

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

/解析器。sh数据。sh模板。txt parsed_file.txt

parsed_file.txt

parsed_file.txt

Variable 1 value: value 1
Variable 2 value: value 2

#6


9  

here's my solution with perl based on former answer, replaces environment variables:

下面是我基于以前答案的perl解决方案,替换了环境变量:

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

#7


6  

If you are open to using Perl, that would be my suggestion. Although there are probably some sed and/or AWK experts that probably know how to do this much easier. If you have a more complex mapping with more than just dbName for your replacements you could extend this pretty easily, but you might just as well put it into a standard Perl script at that point.

如果您愿意使用Perl,这就是我的建议。尽管可能有一些sed和/或AWK专家知道如何更容易地完成这项工作。如果您有一个更复杂的映射,其中不仅包含用于替换的dbName,那么您可以很容易地扩展这个映射,但是您也可以将它放到一个标准的Perl脚本中。

perl -p -e 's/\$\{dbName\}/testdb/s' yourfile | mysql

A short Perl script to do something slightly more complicated (handle multiple keys):

一个简短的Perl脚本来做一些稍微复杂的事情(处理多个键):

#!/usr/bin/env perl
my %replace = ( 'dbName' => 'testdb', 'somethingElse' => 'fooBar' );
undef $/;
my $buf = <STDIN>;
$buf =~ s/\$\{$_\}/$replace{$_}/g for keys %replace;
print $buf;

If you name the above script as replace-script, it could then be used as follows:

如果您将上面的脚本命名为replace-script,则可以如下使用:

replace-script < yourfile | mysql

#8


5  

Here's a robust Bash function that - despite using eval - should be safe to use.

这里有一个健壮的Bash函数——尽管使用eval——应该是安全的。

All ${varName} variable references in the input text are expanded based on the calling shell's variables.

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

Nothing else is expanded: neither variable references whose names are not enclosed in {...} (such as $varName), nor command substitutions ($(...) and legacy syntax `...`), nor arithmetic substitutions ($((...)) and legacy syntax $[...]).

没有其他的扩展:没有包含名称的变量引用在{…}(例如$varName)、命令替换($(…)和遗留语法“…”),以及算术替换($(…))和遗留语法$[…]。

To treat a $ as a literal, \-escape it; e.g.:\${HOME}

把一美元当作字面意义,把它转义;例如:\ $ {回家}

Note that input is only accepted via stdin.

注意,输入只能通过stdin接受。

Example:

例子:

$ expandVarsStrict <<<'$HOME is "${HOME}"; `date` and \$(ls)' # only ${HOME} is expanded
$HOME is "/Users/jdoe"; `date` and $(ls)

Function source code:

函数的源代码:

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
}

The function assumes that no 0x1, 0x2, 0x3, and 0x4 control characters are present in the input, because those chars. are used internally - since the function processes text, that should be a safe assumption.

函数假设输入中不存在0x1、0x2、0x3和0x4控制字符,因为这些字符。在内部使用—由于函数处理文本,这应该是一个安全的假设。

#9


4  

file.tpl:

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:

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

#10


4  

Create rendertemplate.sh:

创建rendertemplate.sh:

#!/usr/bin/env bash

eval "echo \"$(cat $1)\""

And template.tmpl:

和template.tmpl:

Hello, ${WORLD}
Goodbye, ${CHEESE}

Render the template:

呈现模板:

$ export WORLD=Foo
$ CHEESE=Bar ./rendertemplate.sh template.tmpl 
Hello, Foo
Goodbye, Bar

#11


3  

I would suggest using something like Sigil: https://github.com/gliderlabs/sigil

我建议使用类似Sigil的东西:https://github.com/gliderlabs/sigil

It is compiled to a single binary, so it's extremely easy to install on systems.

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

Then you can do a simple one-liner like the following:

然后你可以做一个简单的一行:

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

This is much safer than eval and easier then using regex or sed

这比eval安全得多,使用regex或sed也更容易

#12


2  

I found this thread while wondering the same thing. It inspired me to this (careful with the backticks)

我发现了这条线,同时也在想同样的事情。这让我想到了这个(小心翼翼的背后)

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

#13


2  

Lots of choices here, but figured I'd toss mine on the heap. It is perl based, only targets variables of the form ${...}, takes the file to process as an argument and outputs the converted file on stdout:

这里有很多选择,但我想把我的扔到堆上。它是基于perl的,只针对${…},将文件作为参数进行处理,并将转换后的文件输出到stdout:

use Env;
Env::import();

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

print "$text";

Of course I'm not really a perl person, so there could easily be a fatal flaw (works for me though).

当然,我并不是perl高手,因此很容易出现致命的缺陷(尽管对我来说是可行的)。

#14


1  

It can be done in bash itself if you have control of the configuration file format. You just need to source (".") the configuration file rather than subshell it. That ensures the variables are created in the context of the current shell (and continue to exist) rather than the subshell (where the variable disappear when the subshell exits).

如果您能够控制配置文件格式,那么可以在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

If your config file cannot be a shell script, you can just 'compile' it before executing thus (the compilation depends on your input format).

如果配置文件不是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

In your specific case, you could use something like:

在您的具体案例中,您可以使用以下内容:

$ 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

Then pipe the output of go.bash into MySQL and voila, hopefully you won't destroy your database :-).

然后输出go。bash进入MySQL和voila,希望您不会破坏您的数据库:-)。

#1


113  

Sed!

Sed !

Given template.txt:

鉴于template.txt:

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

we just have to say:

我们不得不说:

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

Thanks to Jonathan Leffler for the tip to pass multiple -e arguments to the same sed invocation.

感谢Jonathan Leffler的建议,将多个-e参数传递给同一个sed调用。

#2


100  

Update

Here is a solution from yottatsa on a similar question that only does replacement for variables like $VAR or ${VAR}, and is a brief one-liner

这里是yottatsa提出的一个类似问题的解决方案,它只替换变量$VAR或${VAR},并且是一个简短的一行程序。

i=32 word=foo envsubst < template.txt

Of course if i and word are in your environment, then it is just

当然,如果我和word在你的环境中,那么它就是公正的

envsubst < template.txt

On my Mac it looks like it was installed as part of gettext and from MacGPG2

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

Old Answer

Here is an improvement to the solution from mogsie on a similar question, my solution does not require you to escale double quotes, mogsie's does, but his is a one liner!

这是来自mogsie的一个类似问题的解决方案,我的解决方案不需要您使用escale双引号,mogsie的方法,但是他的是一个一行!

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

The power on these two solutions is that you only get a few types of shell expansions that don't occur normally $((...)), `...`, and $(...), though backslash is an escape character here, but you don't have to worry that the parsing has a bug, and it does multiple lines just fine.

这两种解决方案的强大之处在于,您只获得了少数几种通常不会出现的shell扩展。,还有$(…),虽然反斜杠在这里是一个转义字符,但是您不必担心解析有一个bug,而且它可以做很多行。

#3


41  

Use /bin/sh. Create a small shell script that sets the variables, and then parse the template using the shell itself. Like so (edit to handle newlines correctly):

使用/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

#4


17  

I was thinking about this again, given the recent interest, and I think that the tool that I was originally thinking of was m4, the macro processor for autotools. So instead of the variable I originally specified, you'd use:

考虑到最近的兴趣,我再次考虑这个问题,我认为我最初考虑的工具是m4,自动工具的宏处理器。所以不是我原先指定的变量,你可以用:

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

#5


11  

template.txt

template.txt

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

data.sh

data.sh

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

parser.sh

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

/解析器。sh数据。sh模板。txt parsed_file.txt

parsed_file.txt

parsed_file.txt

Variable 1 value: value 1
Variable 2 value: value 2

#6


9  

here's my solution with perl based on former answer, replaces environment variables:

下面是我基于以前答案的perl解决方案,替换了环境变量:

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

#7


6  

If you are open to using Perl, that would be my suggestion. Although there are probably some sed and/or AWK experts that probably know how to do this much easier. If you have a more complex mapping with more than just dbName for your replacements you could extend this pretty easily, but you might just as well put it into a standard Perl script at that point.

如果您愿意使用Perl,这就是我的建议。尽管可能有一些sed和/或AWK专家知道如何更容易地完成这项工作。如果您有一个更复杂的映射,其中不仅包含用于替换的dbName,那么您可以很容易地扩展这个映射,但是您也可以将它放到一个标准的Perl脚本中。

perl -p -e 's/\$\{dbName\}/testdb/s' yourfile | mysql

A short Perl script to do something slightly more complicated (handle multiple keys):

一个简短的Perl脚本来做一些稍微复杂的事情(处理多个键):

#!/usr/bin/env perl
my %replace = ( 'dbName' => 'testdb', 'somethingElse' => 'fooBar' );
undef $/;
my $buf = <STDIN>;
$buf =~ s/\$\{$_\}/$replace{$_}/g for keys %replace;
print $buf;

If you name the above script as replace-script, it could then be used as follows:

如果您将上面的脚本命名为replace-script,则可以如下使用:

replace-script < yourfile | mysql

#8


5  

Here's a robust Bash function that - despite using eval - should be safe to use.

这里有一个健壮的Bash函数——尽管使用eval——应该是安全的。

All ${varName} variable references in the input text are expanded based on the calling shell's variables.

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

Nothing else is expanded: neither variable references whose names are not enclosed in {...} (such as $varName), nor command substitutions ($(...) and legacy syntax `...`), nor arithmetic substitutions ($((...)) and legacy syntax $[...]).

没有其他的扩展:没有包含名称的变量引用在{…}(例如$varName)、命令替换($(…)和遗留语法“…”),以及算术替换($(…))和遗留语法$[…]。

To treat a $ as a literal, \-escape it; e.g.:\${HOME}

把一美元当作字面意义,把它转义;例如:\ $ {回家}

Note that input is only accepted via stdin.

注意,输入只能通过stdin接受。

Example:

例子:

$ expandVarsStrict <<<'$HOME is "${HOME}"; `date` and \$(ls)' # only ${HOME} is expanded
$HOME is "/Users/jdoe"; `date` and $(ls)

Function source code:

函数的源代码:

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
}

The function assumes that no 0x1, 0x2, 0x3, and 0x4 control characters are present in the input, because those chars. are used internally - since the function processes text, that should be a safe assumption.

函数假设输入中不存在0x1、0x2、0x3和0x4控制字符,因为这些字符。在内部使用—由于函数处理文本,这应该是一个安全的假设。

#9


4  

file.tpl:

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:

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

#10


4  

Create rendertemplate.sh:

创建rendertemplate.sh:

#!/usr/bin/env bash

eval "echo \"$(cat $1)\""

And template.tmpl:

和template.tmpl:

Hello, ${WORLD}
Goodbye, ${CHEESE}

Render the template:

呈现模板:

$ export WORLD=Foo
$ CHEESE=Bar ./rendertemplate.sh template.tmpl 
Hello, Foo
Goodbye, Bar

#11


3  

I would suggest using something like Sigil: https://github.com/gliderlabs/sigil

我建议使用类似Sigil的东西:https://github.com/gliderlabs/sigil

It is compiled to a single binary, so it's extremely easy to install on systems.

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

Then you can do a simple one-liner like the following:

然后你可以做一个简单的一行:

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

This is much safer than eval and easier then using regex or sed

这比eval安全得多,使用regex或sed也更容易

#12


2  

I found this thread while wondering the same thing. It inspired me to this (careful with the backticks)

我发现了这条线,同时也在想同样的事情。这让我想到了这个(小心翼翼的背后)

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

#13


2  

Lots of choices here, but figured I'd toss mine on the heap. It is perl based, only targets variables of the form ${...}, takes the file to process as an argument and outputs the converted file on stdout:

这里有很多选择,但我想把我的扔到堆上。它是基于perl的,只针对${…},将文件作为参数进行处理,并将转换后的文件输出到stdout:

use Env;
Env::import();

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

print "$text";

Of course I'm not really a perl person, so there could easily be a fatal flaw (works for me though).

当然,我并不是perl高手,因此很容易出现致命的缺陷(尽管对我来说是可行的)。

#14


1  

It can be done in bash itself if you have control of the configuration file format. You just need to source (".") the configuration file rather than subshell it. That ensures the variables are created in the context of the current shell (and continue to exist) rather than the subshell (where the variable disappear when the subshell exits).

如果您能够控制配置文件格式,那么可以在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

If your config file cannot be a shell script, you can just 'compile' it before executing thus (the compilation depends on your input format).

如果配置文件不是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

In your specific case, you could use something like:

在您的具体案例中,您可以使用以下内容:

$ 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

Then pipe the output of go.bash into MySQL and voila, hopefully you won't destroy your database :-).

然后输出go。bash进入MySQL和voila,希望您不会破坏您的数据库:-)。