200

我想将“模板”文件的输出通过管道传输到 MySQL,该文件具有${dbName}散布的变量。替换这些实例并将输出转储到标准输出的命令行实用程序是什么?

4

18 回答 18

232

赛德

给定模板.txt:

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

我们只需要说:

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

感谢 Jonathan Leffler 提供的将多个-e参数传递给同一个sed调用的技巧。

于 2009-01-06T08:01:21.887 回答
231

更新

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

i=32 word=foo envsubst < template.txt

当然如果iword在你的环境中,那么它只是

envsubst < template.txt

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

旧答案

这是mogsie在类似问题上对解决方案的改进,我的解决方案不需要您升级双引号,mogsie 的,但他是单引号!

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

这两种解决方案的强大之处在于,您只会得到几种通常不会发生的 shell 扩展 $((...))、`...` 和 $(...),尽管反斜杠此处转义字符,但您不必担心解析有错误,并且它可以很好地执行多行。

于 2013-06-10T19:03:05.807 回答
49

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

文件模板.txt:

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

文件脚本.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
于 2009-01-06T07:10:06.593 回答
24

考虑到最近的兴趣,我再次考虑这个问题,我认为我最初想到的工具是m4autotools 的宏处理器。因此,您可以使用以下变量,而不是我最初指定的变量:

$echo 'I am a DBNAME' | m4 -DDBNAME="database name"
于 2011-05-19T15:19:00.497 回答
21

创建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
于 2017-02-22T09:01:44.700 回答
16

模板.txt

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

数据.sh

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

解析器.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
于 2009-08-20T21:31:19.467 回答
13

这是一个强大的 Bash 函数——尽管使用了eval——应该可以安全使用。

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

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

要将 a$视为文字,\请 - 转义它;例如:\${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控制字符,因为这些字符。在内部使用 - 因为函数处理text,这应该是一个安全的假设。

于 2016-10-21T03:31:10.960 回答
12

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

perl -p -e 's/\$\{(\w+)\}/(exists $ENV{$1}?$ENV{$1}:"missing variable $1")/eg' < infile > outfile
于 2013-06-06T15:32:34.190 回答
6

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

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

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

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

这比eval使用正则表达式或sed

于 2016-02-23T20:44:52.233 回答
6

这是一种让 shell 为您进行替换的方法,就好像文件的内容是在双引号之间键入的一样。

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

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

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

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

解释:

  • iword作为环境变量传递给sh.
  • sh执行它传递的字符串的内容。
  • 彼此相邻写入的字符串变成一个字符串,该字符串是:
    • ' echo "' + " $(cat template.txt)" + ' "'
  • 由于替换是在 之间",“ $(cat template.txt)”成为 的输出cat template.txt
  • 所以执行的命令sh -c就变成了:
    • echo "The number is ${i}\nThe word is ${word}",
    • 其中iword是指定的环境变量。
于 2019-03-30T03:03:41.163 回答
5

如果您愿意使用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
于 2009-01-06T07:08:45.003 回答
4

文件.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.

脚本.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
于 2011-10-03T10:08:31.220 回答
2

我在想同样的事情时发现了这个线程。它启发了我(小心反引号)

$ echo $MYTEST
pass!
$ cat FILE
hello $MYTEST world
$ eval echo `cat FILE`
hello pass! world
于 2011-05-16T04:09:11.057 回答
2

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

use Env;
Env::import();

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

print "$text";

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

于 2014-07-12T01:55:27.380 回答
1

如果您可以控制配置文件格式,则可以在 bash 本身中完成。您只需要 source (".") 配置文件而不是 subshel​​l。这确保了在当前 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 和瞧,希望你不会破坏你的数据库:-)。

于 2009-01-06T07:49:59.117 回答
0

使用备份对可能的多个文件进行 perl 编辑。

  perl -e 's/\$\{([^}]+)\}/defined $ENV{$1} ? $ENV{$1} : ""/eg' \
    -i.orig \
    -p config/test/*
于 2020-02-03T21:12:34.107 回答
0

我创建了一个名为shtpl. 我shtpl使用类似 jinja 的语法,现在我经常使用 ansible,我非常熟悉:

$ cat /tmp/test
{{ aux=4 }}
{{ myarray=( a b c d ) }}
{{ A_RANDOM=$RANDOM }}
$A_RANDOM
{% if $(( $A_RANDOM%2 )) == 0 %}
$A_RANDOM is even
{% else %}
$A_RANDOM is odd
{% endif %}
{% if $(( $A_RANDOM%2 )) == 0 %}
{% for n in 1 2 3 $aux %}
\$myarray[$((n-1))]: ${myarray[$((n-1))]}
/etc/passwd field #$n: $(grep $USER /etc/passwd | cut -d: -f$n)
{% endfor %}
{% else %}
{% for n in {1..4} %}
\$myarray[$((n-1))]: ${myarray[$((n-1))]}
/etc/group field #$n: $(grep ^$USER /etc/group | cut -d: -f$n)
{% endfor %}
{% endif %}


$ ./shtpl < /tmp/test
6535
6535 is odd
$myarray[0]: a
/etc/group field #1: myusername
$myarray[1]: b
/etc/group field #2: x
$myarray[2]: c
/etc/group field #3: 1001
$myarray[3]: d
/etc/group field #4: 

更多信息在我的github

于 2021-01-14T08:25:57.087 回答
0

对我来说,这是最简单和最强大的解决方案,您甚至可以使用相同的命令包含其他模板eval echo "$(<template.txt)

嵌套模板示例

  1. 创建模板文件,变量采用常规 bash 语法${VARIABLE_NAME}$VARIABLE_NAME

您必须在模板中转义特殊字符,\否则它们将被eval.

template.txt

Hello ${name}!
eval echo $(<nested-template.txt)

nested-template.txt

Nice to have you here ${name} :\)
  1. 创建源文件

template.source

declare name=royman 
  1. 解析模板
source template.source && eval echo "$(<template.txt)"
  1. 输出
Hello royman!
Nice to have you here royman :)

于 2022-02-19T18:18:04.687 回答