Bash模板:如何使用Bash从模板构建配置文件?


134

我正在编写一个脚本,为自己的Web服务器自动为Apache和PHP创建配置文件。我不想使用任何CPanel或ISPConfig之类的GUI。

我有一些Apache和PHP配置文件的模板。Bash脚本需要读取模板,进行变量替换并将已解析的模板输出到某个文件夹中。最好的方法是什么?我可以想到几种方式。哪一个最好,或者可能有更好的方法呢?我想在纯Bash中做到这一点(例如,在PHP中很简单)

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

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"

顺便说一句,我如何在这里将输出重定向到外部文件?如果变量包含引号,我是否需要转义?

2)使用cat&sed将每个变量替换为其值:

给定template.txt:

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

命令:

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

对于我来说,这似乎很糟糕,因为需要转义许多不同的符号,并且带有许多变量,所以该行太长了。

您能想到其他一些优雅安全的解决方案吗?


Answers:


61

您可以使用此:

perl -p -i -e 's/\$\{([^}]+)\}/defined $ENV{$1} ? $ENV{$1} : $&/eg' < template.txt

${...}用相应的环境变量替换所有字符串(运行此脚本之前不要忘记导出它们)。

对于纯bash,这应该可以工作(假设变量不包含$ {...}字符串):

#!/bin/bash
while read -r line ; do
    while [[ "$line" =~ (\$\{[a-zA-Z_][a-zA-Z_0-9]*\}) ]] ; do
        LHS=${BASH_REMATCH[1]}
        RHS="$(eval echo "\"$LHS\"")"
        line=${line//$LHS/$RHS}
    done
    echo "$line"
done

。如果RHS引用一些引用自身的变量,则该解决方案不会挂起:

#!/bin/bash
line="$(cat; echo -n a)"
end_offset=${#line}
while [[ "${line:0:$end_offset}" =~ (.*)(\$\{([a-zA-Z_][a-zA-Z_0-9]*)\})(.*) ]] ; do
    PRE="${BASH_REMATCH[1]}"
    POST="${BASH_REMATCH[4]}${line:$end_offset:${#line}}"
    VARNAME="${BASH_REMATCH[3]}"
    eval 'VARVAL="$'$VARNAME'"'
    line="$PRE$VARVAL$POST"
    end_offset=${#PRE}
done
echo -n "${line:0:-1}"

警告:我不知道一种正确处理bash中NUL的输入或保留尾随换行符数量的方法。出现最后一个变体是因为壳“喜欢”二进制输入:

  1. read 将解释反斜杠。
  2. read -r 不会解释反斜杠,但如果不以换行符结尾,仍会删除最后一行。
  3. "$(…)"因为有本将去除尽可能多的尾随换行符,所以我最终; echo -n a和使用echo -n "${line:0:-1}":此下降的最后一个字符(这是a)和蜜饯尽可能多的尾随换行符有一个在输入(包括无)。

3
我会改变[^}],以[A-Za-Z_][A-Za-z0-9_]在bash的版本,以防止外壳超越严格的替换(例如,如果它试图处理${some_unused_var-$(rm -rf $HOME)})。
克里斯·约翰森

2
@FractalizeR,您可能需要$&在perl解决方案中更改为""${...}如果无法替换,则首先保持不变,然后将其替换为空字符串。
ZyX

5
注意:显然a从bash 3.1更改为3.2(及更高版本),其中正则表达式周围的单引号-将正则表达式的内容视为字符串文字。因此,上述正则表达式应为...(\ $ \ {[a-zA-Z _] [a-zA-Z_0-9] * \})stackoverflow.com/questions/304864/…–
Blue Waters

2
要使while循环即使没有换行符终止也能读取最后一行,请使用while read -r line || [[ -n $line ]]; do。另外,您的read命令从每行中去除前导和尾随空格;为了避免这种情况,请使用while IFS= read -r line || [[ -n $line ]]; do
mklement0 2015年

2
只是为那些寻求全面解决方案的人提供了一个约束:这些本来就方便的解决方案不允许您有选择地保护变量引用免于扩展(例如,通过\ 对它们进行转义)。
mklement0

138

尝试 envsubst

FOO=foo
BAR=bar
export FOO BAR

envsubst <<EOF
FOO is $FOO
BAR is $BAR
EOF

11
仅供参考,envsubst使用heredoc时不需要,因为bash将heredoc视为文字双引号字符串,并且已经在其中插入变量。但是,当您想从另一个文件读取模板时,这是一个不错的选择。麻烦得多的好替代品m4
beporter

2
了解此命令令我感到非常惊喜。我试图手动将envsubst的功能拼凑成零成功。谢谢yottatsa!
蒂姆·斯图尔特

4
注意:这envsubst是一个GNU gettext实用程序,实际上并不是那么健壮(因为gettext是用于本地化人类消息的)。最重要的是,它无法识别反斜杠转义的$ {VAR}替换(因此,您将无法获得在运行时使用$ VAR替换的模板,例如Shell脚本或Nginx conf文件)。请参阅我的答案以获取解决反斜杠转义的解决方案。
Stuart P. Bentley

4
@beporter在这种情况下,如果您出于某种原因希望将此模板传递给envsubst,则需要使用<<"EOF",它不会插值变量(引号的终止符类似于heredocs 的单引号)。
Stuart P. Bentley

2
我使用它的方式是: cat template.txt | envsubst
Trudadjustr

47

envsubst对我来说是新的。太棒了

记录下来,使用heredoc是配置conf文件的好方法。

STATUS_URI="/hows-it-goin";  MONITOR_IP="10.10.2.15";

cat >/etc/apache2/conf.d/mod_status.conf <<EOF
<Location ${STATUS_URI}>
    SetHandler server-status
    Order deny,allow
    Deny from all
    Allow from ${MONITOR_IP}
</Location>
EOF

1
我更喜欢此方法,而不是envsubst因为它使我从apt-get install gettext-baseDockerfile中的其他文件中省了下来
trueadjustr

但是,该外壳作为类似于模板的脚本,无需安装任何外部库,也不会因处理棘手的表达式而感到压力。
千木郷

35

我同意使用sed:这是搜索/替换的最佳工具。这是我的方法:

$ cat template.txt
the number is ${i}
the dog's name is ${name}

$ cat replace.sed
s/${i}/5/
s/${name}/Fido/

$ sed -f replace.sed template.txt > out.txt

$ cat out.txt
the number is 5
the dog's name is Fido

1
这需要用于替换字符串的临时文件,对吗?有没有临时文件的方法吗?
弗拉迪斯拉夫·拉斯特鲁尼(Fladislav Rastrusny),2010年

@FractalizeR:某些版本的sed具有-i类似于perl选项的选项(已编辑文件)。检查您的sed的联机帮助页。
克里斯·约翰森

@FractalizeR是的,sed -i将替换内联。如果您熟悉的Tcl(另一种脚本语言),然后检查了这个线程stackoverflow.com/questions/2818130/...
海武

我通过以下sed命令的属性文件创建了replace.sed:sed -e's / ^ / s \ / $ {/ g'-e's / = /} \ // g'-e's / $ / \ // g'the.properties> replace.sed
Jaap D

@hai vu的代码创建一个sed程序,并使用sed的-f标志传递该程序。如果需要,可以改为使用-e标志将sed程序的每一行传递到sed中。FWIW我喜欢使用sed进行模板制作的想法。
安德鲁·奥尔布赖特

23

我认为评估非常有效。它处理带有换行符,空格和各种bash内容的模板。当然,如果您完全控制模板本身:

$ cat template.txt
variable1 = ${variable1}
variable2 = $variable2
my-ip = \"$(curl -s ifconfig.me)\"

$ echo $variable1
AAA
$ echo $variable2
BBB
$ eval "echo \"$(<template.txt)\"" 2> /dev/null
variable1 = AAA
variable2 = BBB
my-ip = "11.22.33.44"

当然,应谨慎使用此方法,因为eval可以执行任意代码。以root身份运行几乎是不可能的。模板中的引号需要转义,否则将被吞噬eval

如果您愿意cat,也可以使用此处文档echo

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

@plockc提出了避免bash引用转义问题的解决方案:

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

编辑:删除了有关使用sudo以root身份运行此文件的部分。

编辑:添加了有关如何需要引号的注释,添加了plockc的解决方案!


这会删除模板中的引号,并且不会替换单引号内的引号,因此,取决于模板格式,可能会导致细微的错误。但是,这可能适用于任何基于Bash的模板方法。
2014年

恕我直言,基于Bash的模板是疯狂的,因为您需要成为bash程序员才能了解您的模板在做什么!但感谢您的评论!
mogsie

@AlexB:这种方法 在单引号之间进行替换,因为它们只是封闭的双引号字符串内的文字字符,而不是在evaled echo / cat命令处理它们时的字符串定界符;尝试一下eval "echo \"'\$HOME'\""
mklement0

21

我有一个像mogsie这样的bash解决方案,但是使用了Heredoc而不是herestring,可以避免转义双引号

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

4
此解决方案支持模板中的Bash参数扩展。我的最爱是必需参数${param:?}可选参数周围嵌套文本。示例:${DELAY:+<delay>$DELAY</delay>}未定义DELAY时扩展为空,而当DELAY = 17时扩展为<delay> 17 </ delay>。
埃里克·伯林格

2
哦! EOF分隔符可以使用动态字符串,例如PID _EOF_$$
埃里克·伯林格

1
@ mklement0尾随换行符的一种解决方法是使用某种扩展,例如,一个空变量$trailing_newline,或者使用$NL5并确保将其扩展为5个换行符。
xebeche

@xebeche:是的,你将在建议非常到底里面template.txt会为了工作,以保留尾随换行。
mklement0 2016年

1
一个不错的解决方案,但是请注意,命令替换将删除输入文件中的所有尾随换行符,尽管通常不会出现问题。另一个极端情况:由于使用eval,如果template.txt包含EOF一行,它会过早终止here-doc,从而中断命令。(将帽子提示给@xebeche)。
mklement0

18

编辑2017年1月6日

我需要在配置文件中保留双引号,因此使用sed将双引号转义为双引号有助于:

render_template() {
  eval "echo \"$(sed 's/\"/\\\\"/g' $1)\""
}

我想不出要跟上新行,但要保留它们之间的空行。


尽管这是一个古老的话题,但IMO我在这里找到了更优雅的解决方案:http : //pempek.net/articles/2013/07/08/bash-sh-as-template-engine/

#!/bin/sh

# render a template configuration file
# expand variables + preserve formatting
render_template() {
  eval "echo \"$(cat $1)\""
}

user="Gregory"
render_template /path/to/template.txt > path/to/configuration_file

所有功劳归GrégoryPakosz所有。


这将从输入中删除双引号,并且如果输入文件中有多个结尾的换行符,则将它们替换为一个单引号。
mklement0

2
我需要减少两个反斜线才能使其正常运行,即 eval "echo \"$(sed 's/\"/\\"/g' $1)\""
David Bau

不幸的是,这种方法不允许您模板化php文件(它们包含$variables)。
IStranger

10

使用envsubst代替重新发明轮子 几乎可以在任何情况下使用,例如,通过docker容器中的环境变量构建配置文件。

如果在Mac上,请确保您有自制软件,然后从gettext链接它:

brew install gettext
brew link --force gettext

./template.cfg

# We put env variables into placeholders here
this_variable_1 = ${SOME_VARIABLE_1}
this_variable_2 = ${SOME_VARIABLE_2}

./.env:

SOME_VARIABLE_1=value_1
SOME_VARIABLE_2=value_2

./configure.sh

#!/bin/bash
cat template.cfg | envsubst > whatever.cfg

现在使用它:

# make script executable
chmod +x ./configure.sh
# source your variables
. .env
# export your variables
# In practice you may not have to manually export variables 
# if your solution depends on tools that utilise .env file 
# automatically like pipenv etc. 
export SOME_VARIABLE_1 SOME_VARIABLE_2
# Create your config file
./configure.sh

这个调用顺序envsubst实际上是有效的。
亚历克斯(Alex)

对于envsubst在MacOS上无法正常运行的其他用户,您需要使用homebrew:安装brew install gettext
马特

9

可接受答案的更长但更可靠的版本:

perl -pe 's;(\\*)(\$([a-zA-Z_][a-zA-Z_0-9]*)|\$\{([a-zA-Z_][a-zA-Z_0-9]*)\})?;substr($1,0,int(length($1)/2)).($2&&length($1)%2?$2:$ENV{$3||$4});eg' template.txt

这会展开或的所有实例$VAR ${VAR}他们的环境值(或者,如果他们未定义,空字符串)。

它正确地转义了反斜杠,并接受了反斜杠转义的$以禁止替换(与envsubst不同,事实证明,envsubst 不会这样做)。

因此,如果您的环境是:

FOO=bar
BAZ=kenny
TARGET=backslashes
NOPE=engi

您的模板是:

Two ${TARGET} walk into a \\$FOO. \\\\
\\\$FOO says, "Delete C:\\Windows\\System32, it's a virus."
$BAZ replies, "\${NOPE}s."

结果将是:

Two backslashes walk into a \bar. \\
\$FOO says, "Delete C:\Windows\System32, it's a virus."
kenny replies, "${NOPE}s."

如果只想在$之前转义反斜杠(您可以在模板中不变地编写“ C:\ Windows \ System32”),请使用此稍作修改的版本:

perl -pe 's;(\\*)(\$([a-zA-Z_][a-zA-Z_0-9]*)|\$\{([a-zA-Z_][a-zA-Z_0-9]*)\});substr($1,0,int(length($1)/2)).(length($1)%2?$2:$ENV{$3||$4});eg' template.txt

8

我会这样做,可能效率较低,但更易于阅读/维护。

TEMPLATE='/path/to/template.file'
OUTPUT='/path/to/output.file'

while read LINE; do
  echo $LINE |
  sed 's/VARONE/NEWVALA/g' |
  sed 's/VARTWO/NEWVALB/g' |
  sed 's/VARTHR/NEWVALC/g' >> $OUTPUT
done < $TEMPLATE

10
您无需逐行阅读并且只需一次sed调用即可完成此操作:sed -e 's/VARONE/NEWVALA/g' -e 's/VARTWO/NEWVALB/g' -e 's/VARTHR/NEWVALC/g' < $TEMPLATE > $OUTPUT
Brandon Bloom 2013年

8

如果要使用Jinja2模板,请参见以下项目:j2cli

它支持:

  • JSON,INI,YAML文件和输入流中的模板
  • 根据环境变量进行模板

5

使用纯bash从ZyX那里获得答案,但是使用新型正则表达式匹配和间接参数替换,它变成:

#!/bin/bash
regex='\$\{([a-zA-Z_][a-zA-Z_0-9]*)\}'
while read line; do
    while [[ "$line" =~ $regex ]]; do
        param="${BASH_REMATCH[1]}"
        line=${line//${BASH_REMATCH[0]}/${!param}}
    done
    echo $line
done

5

如果使用Perl是一种选择,并且您只满足于基于环境变量(而不是所有shell变量)的扩展,请考虑Stuart P. Bentley的可靠答案

该答案旨在提供一种使用bash的解决方案,尽管使用了该解决方案,但eval仍应可以安全使用

目标是:

  • 支持扩展引用${name}$name变量引用。
  • 防止其他所有扩展:
    • 命令替换($(...)和旧式语法`...`
    • 算术替代($((...))和传统语法$[...])。
  • 通过以\(为前缀,可以有选择地抑制变量扩展\${name})。
  • 保留特殊字符。在输入中,尤其是"\实例中。
  • 允许通过参数或通过stdin输入。

功能expandVars()

expandVars() {
  local txtToEval=$* txtToEvalEscaped
  # If no arguments were passed, process stdin input.
  (( $# == 0 )) && IFS= read -r -d '' txtToEval
  # Disable command substitutions and arithmetic expansions to prevent execution
  # of arbitrary commands.
  # Note that selectively allowing $((...)) or $[...] to enable arithmetic
  # expressions is NOT safe, because command substitutions could be embedded in them.
  # If you fully trust or control the input, you can remove the `tr` calls below
  IFS= read -r -d '' txtToEvalEscaped < <(printf %s "$txtToEval" | tr '`([' '\1\2\3')
  # Pass the string to `eval`, escaping embedded double quotes first.
  # `printf %s` ensures that the string is printed without interpretation
  # (after processing by by bash).
  # The `tr` command reconverts the previously escaped chars. back to their
  # literal original.
  eval printf %s "\"${txtToEvalEscaped//\"/\\\"}\"" | tr '\1\2\3' '`(['
}

例子:

$ expandVars '\$HOME="$HOME"; `date` and $(ls)'
$HOME="/home/jdoe"; `date` and $(ls)  # only $HOME was expanded

$ printf '\$SHELL=${SHELL}, but "$(( 1 \ 2 ))" will not expand' | expandVars
$SHELL=/bin/bash, but "$(( 1 \ 2 ))" will not expand # only ${SHELL} was expanded
  • 出于性能原因,该函数读取标准输入 全部入内存,但是很容易使该函数适应逐行方法。
  • 还支持非基本变量扩展,例如${HOME:0:10},只要它们不包含嵌入式命令或算术替换,例如${HOME:0:$(echo 10)}
    • 这种嵌入式替换实际上会破坏功能(因为所有 $(`实例都被盲目地转义了)。
    • 同样,格式错误的变量引用,例如${HOME(缺少关闭})会破坏函数。
  • 由于bash对双引号字符串的处理,反斜杠的处理方式如下:
    • \$name 防止扩展。
    • 一个\不跟单$保留字符。
    • 如果要表示多个相邻 \实例,则必须将它们加倍;例如:
      • \\-> \-与刚才一样\
      • \\\\ -> \\
    • 输入不能包含以下(很少使用)字符,其被用于内部用途:0x10x20x3
  • 很大程度上存在一个假设性的担忧,即如果bash应该引入新的扩展语法,则此函数可能不会阻止此类扩展-请参见下文,了解不使用的解决方案eval

如果您正在寻找一种支持扩展的限制性更强的解决方案${name}(即使用强制花括号忽略$name参考),请参阅我的答案


这是公认的答案的仅bash,eval-bash解决方案改进版本

改进之处包括:

  • 支持扩展引用${name}$name变量引用。
  • 支持对\不应扩展的变量引用进行转义。
  • 与上述eval基于解决方案的解决方案不同,
    • 非基本扩展被忽略
    • 格式错误的变量引用将被忽略(它们不会破坏脚本)
 IFS= read -d '' -r lines # read all input from stdin at once
 end_offset=${#lines}
 while [[ "${lines:0:end_offset}" =~ (.*)\$(\{([a-zA-Z_][a-zA-Z_0-9]*)\}|([a-zA-Z_][a-zA-Z_0-9]*))(.*) ]] ; do
      pre=${BASH_REMATCH[1]} # everything before the var. reference
      post=${BASH_REMATCH[5]}${lines:end_offset} # everything after
      # extract the var. name; it's in the 3rd capture group, if the name is enclosed in {...}, and the 4th otherwise
      [[ -n ${BASH_REMATCH[3]} ]] && varName=${BASH_REMATCH[3]} || varName=${BASH_REMATCH[4]}
      # Is the var ref. escaped, i.e., prefixed with an odd number of backslashes?
      if [[ $pre =~ \\+$ ]] && (( ${#BASH_REMATCH} % 2 )); then
           : # no change to $lines, leave escaped var. ref. untouched
      else # replace the variable reference with the variable's value using indirect expansion
           lines=${pre}${!varName}${post}
      fi
      end_offset=${#pre}
 done
 printf %s "$lines"

5

这是另一个纯bash解决方案:

  • 它使用heredoc,所以:
    • 复杂性不会增加,因为需要额外的语法
    • 模板可以包含bash代码
      • 这也使您可以正确缩进内容。见下文。
  • 它不使用eval,所以:
    • 尾随空行的渲染没有问题
    • 模板中的引号没有问题

$ cat code

#!/bin/bash
LISTING=$( ls )

cat_template() {
  echo "cat << EOT"
  cat "$1"
  echo EOT
}

cat_template template | LISTING="$LISTING" bash

$ cat template (带有尾随的换行符和双引号)

<html>
  <head>
  </head>
  <body> 
    <p>"directory listing"
      <pre>
$( echo "$LISTING" | sed 's/^/        /' )
      <pre>
    </p>
  </body>
</html>

输出

<html>
  <head>
  </head>
  <body> 
    <p>"directory listing"
      <pre>
        code
        template
      <pre>
    </p>
  </body>
</html>

4

这是另一种解决方案:生成一个包含所有变量和模板文件内容的bash脚本,该脚本如下所示:

word=dog           
i=1                
cat << EOF         
the number is ${i} 
the word is ${word}

EOF                

如果我们将此脚本输入bash,它将产生所需的输出:

the number is 1
the word is dog

这是生成该脚本并将其提交到bash的方法:

(
    # Variables
    echo word=dog
    echo i=1

    # add the template
    echo "cat << EOF"
    cat template.txt
    echo EOF
) | bash

讨论区

  • 括号会打开一个子外壳,其目的是将生成的所有输出组合在一起
  • 在子外壳中,我们生成所有变量声明
  • 同样在子外壳中,我们cat使用HEREDOC 生成命令
  • 最后,我们将sub shell输出输入bash并生成所需的输出
  • 如果要将此输出重定向到文件中,请用以下内容替换最后一行:

    ) | bash > output.txt

3

此页面描述了awk答案

awk '{while(match($0,"[$]{[^}]*}")) {var=substr($0,RSTART+2,RLENGTH -3);gsub("[$]{"var"}",ENVIRON[var])}}1' < input.txt > output.txt

这样可以使所有报价保持不变。大!
百事可乐

3

shtpl的完美案例。(我的项目,因此它并未得到广泛使用,并且缺少文档。但是无论如何,这是它提供的解决方案。可能要对其进行测试。)

只需执行:

$ i=1 word=dog sh -c "$( shtpl template.txt )"

结果是:

the number is 1
the word is dog

玩得开心。


1
如果胡扯,无论如何它都被否决了。我对此表示同意。但是,可以肯定地说,它不是清晰可见,实际上是我的项目。将来使它更加可见。谢谢您的宝贵意见和宝贵时间。
zstegi

我想补充一点,就是我昨天确实搜索了用例,其中shtpl将是一个完美的解决方案。是的,我很无聊...
zstegi

3
# Usage: template your_file.conf.template > your_file.conf
template() {
        local IFS line
        while IFS=$'\n\r' read -r line ; do
                line=${line//\\/\\\\}         # escape backslashes
                line=${line//\"/\\\"}         # escape "
                line=${line//\`/\\\`}         # escape `
                line=${line//\$/\\\$}         # escape $
                line=${line//\\\${/\${}       # de-escape ${         - allows variable substitution: ${var} ${var:-default_value} etc
                # to allow arithmetic expansion or command substitution uncomment one of following lines:
#               line=${line//\\\$\(/\$\(}     # de-escape $( and $(( - allows $(( 1 + 2 )) or $( command ) - UNSECURE
#               line=${line//\\\$\(\(/\$\(\(} # de-escape $((        - allows $(( 1 + 2 ))
                eval "echo \"${line}\"";
        done < "$1"
}

这是纯bash函数,可根据您的喜好进行调整,可用于生产环境,并且在任何输入下都不应中断。如果它破裂了-让我知道。



0

这是一个保留空白的bash函数:

# Render a file in bash, i.e. expand environment variables. Preserves whitespace.
function render_file () {
    while IFS='' read line; do
        eval echo \""${line}"\"
    done < "${1}"
}

0

这是perl基于其他一些答案的修改后的脚本:

perl -pe 's/([^\\]|^)\$\{([a-zA-Z_][a-zA-Z_0-9]*)\}/$1.$ENV{$2}/eg' -i template

功能(根据我的需要,但应该易于修改):

  • 跳过转义的参数扩展(例如\ $ {VAR})。
  • 支持格式为$ {VAR}的参数扩展,但不支持$ VAR。
  • 如果没有VAR envar,则用空白字符串替换$ {VAR}。
  • 名称中仅支持z,AZ,0-9和下划线字符(不包括第一个位置的数字)。


0

我对这个奇妙问题的谦虚贡献。

tpl() {
    local file=$(cat - | \
                 sed -e 's/\"/\\"/g' \
                     -e "s/'/\\'/g")
    local vars=$(echo ${@} | tr ' ' ';')
    echo "$(sh -c "${vars};echo \"$file\"")"
}

cat tpl.txt | tpl "one=fish" "two=fish"

这实际上是通过使用subshel​​l进行envar替换而起作用的,除了它不使用eval并且显式转义单引号和双引号。它将var表达式串联成一行,没有空格,不要混淆sh,然后将模板传递给echo,以sh处理var替换。它保留换行符,注释等...,\${like_this}如果您不希望对var进行解释,则可以转义。${missing_var}只会被替换为空值。

这里的许多其他答案都非常好,但是我想要的是非常简单的东西,对于我目前拥有的模板案例,它不需要处理所有可能的案例。

请享用!


0

要遵循plockc在此页面上的回答,以下是适用于仪表板的版本,适合那些希望避免宗教信仰的人。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.