【问题标题】:Bash Templating: How to build configuration files from templates with Bash?Bash 模板:如何使用 Bash 从模板构建配置文件?
【发布时间】:2011-02-24 05:55:30
【问题描述】:

我正在编写一个脚本来自动为我自己的网络服务器创建 Apache 和 PHP 的配置文件。我不想使用任何图形用户界面,如 CPanel 或 ISPConfig。

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

1) How to replace ${} placeholders in a text file?

模板.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 将每个变量替换为其值:

给定模板.txt:

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

命令:

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

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

你能想出其他优雅安全的解决方案吗?

【问题讨论】:

标签: bash templates templating


【解决方案1】:

试试envsubst

FOO=foo
BAR=bar
export FOO BAR

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

【讨论】:

  • 仅供参考,使用heredoc 时不需要envsubst,因为bash 将heredoc 视为文字双引号字符串并已在其中插入变量。但是,当您想从另一个文件中读取模板时,这是一个不错的选择。一个很好的替代更繁琐的m4
  • 得知这个命令我感到非常惊喜。我试图以零成功手动拼凑 envsubst 的功能。谢谢yottatsa!
  • 注意:envsubst 是一个 GNU gettext 实用程序,实际上并不是那么健壮(因为 gettext 用于本地化人类消息)。最重要的是,它不识别反斜杠转义的 ${VAR} 替换(因此您不能拥有在运行时使用 $VAR 替换的模板,例如 shell 脚本或 Nginx conf 文件)。有关处理反斜杠转义的解决方案,请参阅 my answer
  • @beporter 在这种情况下,如果您想将此模板传递给 envsubst 出于某种原因,您需要使用&lt;&lt;"EOF",它插入变量(带引号的终止符就像 heredocs 的单引号)。
  • 我用它喜欢:cat template.txt | envsubst
【解决方案2】:

你可以用这个:

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 输入或保留尾随换行符的数量。最后一个变体是因为 shell“喜欢”二进制输入:

  1. read 将解释反斜杠。
  2. read -r 不会解释反斜杠,但如果最后一行不以换行符结尾,它仍会删除。
  3. "$(…)" 将删除尽可能多的尾随换行符,因此我以 ; echo -n a 结束 并使用 echo -n "${line:0:-1}":这会删除最后一个字符(即 a)并保留尽可能多的尾随换行符在输入中(包括否)。

【讨论】:

  • 我会在 bash 版本中将 [^}] 更改为 [A-Za-Z_][A-Za-z0-9_] 以防止 shell 超出严格替换(例如,如果它试图处理 ${some_unused_var-$(rm -rf $HOME)})。
  • @FractalizeR 您可能希望将 perl 解决方案中的 $&amp; 更改为 "":如果替换失败,首先将 ${...} 保持不变,然后将其替换为空字符串。
  • 注意:显然,从 bash 3.1 到 3.2(及更高版本)发生了变化,其中正则表达式周围的单引号 - 将正则表达式的内容视为字符串文字。所以上面的正则表达式应该是... (\$\{[a-zA-Z_][a-zA-Z_0-9]*\}) stackoverflow.com/questions/304864/…
  • 要使while 循环读取最后一行,即使它没有被换行符终止,请使用while read -r line || [[ -n $line ]]; do。此外,您的 read 命令会从每行删除前导和尾随空格;为避免这种情况,请使用while IFS= read -r line || [[ -n $line ]]; do
  • 请注意对于那些寻求综合解决方案的人的限制:这些其他方便的解决方案不允许您有选择地保护变量引用不被扩展(例如通过\ -转义它们)。跨度>
【解决方案3】:

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

【讨论】:

  • 我比 envsubst 更喜欢这个,因为它从我的 Dockerfile 中额外的 apt-get install gettext-base 中拯救了我
  • shell 是一个类似模板的脚本,但是没有任何外部库安装,也没有应对棘手的表达式的压力。
  • 我的首选解决方案!
【解决方案4】:

我同意使用 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

【讨论】:

  • 这需要临时文件来替换字符串,对吧?有没有办法在没有临时文件的情况下做到这一点?
  • @FractalizeR:某些版本的 sed 有一个类似于 perl 选项的-i 选项(就地编辑文件)。检查您的 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
  • @hai vu 的代码创建了一个 sed 程序并使用 sed 的 -f 标志传递该程序。如果需要,您可以改为使用 -e 标志将 sed 程序的每一行传入 sed。 FWIW 我喜欢使用 sed 进行模板的想法。
【解决方案5】:

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

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

【讨论】:

  • 此方案支持模板中的Bash parameter expansion。我最喜欢的是带有${param:?}必需参数 和嵌套文本around 可选参数。示例:${DELAY:+&lt;delay&gt;$DELAY&lt;/delay&gt;} 在 DELAY 未定义时扩展为空,在 DELAY=17 时扩展为 17
  • 哦!并且 EOF 分隔符可以使用动态字符串,例如 PID _EOF_$$
  • @mklement0 尾随换行符的解决方法是使用一些扩展,例如一个空变量$trailing_newline,或使用$NL5 并确保它扩展为5 个换行符。
  • @xebeche:是的,将您建议的内容放在最后template.txt 内可以保留尾随换行符。
  • 一个优雅的解决方案,但请注意,命令替换将从输入文件中删除任何尾随换行符,尽管这通常不是问题。另一个极端情况:由于使用了eval,如果template.txt 在其自身的一行中包含EOF,它将提前终止here-doc,从而中断命令。 (向@xebeche 致敬)。
【解决方案6】:

我认为 eval 效果很好。它处理带有换行符、空格和各种 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 的模板方法。
  • 恕我直言,基于 Bash 的模板太疯狂了,因为您需要成为 bash 程序员才能了解您的模板在做什么!但是感谢您的评论!
  • @AlexB:这种方法在单引号之间替换,因为它们只是封闭的双引号字符串中的文字字符,而不是evaled 时的字符串分隔符echo / cat 命令处理它们;试试eval "echo \"'\$HOME'\""
【解决方案7】:

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égory Pakosz

【讨论】:

  • 这会从输入中删除双引号,如果输入文件中有多个尾随换行符,则将它们替换为一个。
  • 我需要减少两个反斜杠才能使其工作,即eval "echo \"$(sed 's/\"/\\"/g' $1)\""
  • 不幸的是,这种方法不允许您模板化 php 文件(它们包含 $variables)。
【解决方案8】:

使用 envsubst 而不是重新发明轮子 几乎可以在任何场景中使用,例如从 docker 容器中的环境变量构建配置文件。

如果在 mac 上确保你有 homebrew 然后从 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的调用序列确实有效。
  • 对于其他人来说,envsubst 在 MacOS 上不起作用,您需要使用自制软件安装它:brew install gettext
【解决方案9】:

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

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

【讨论】:

  • 您可以在不逐行阅读且只调用一次 sed 的情况下执行此操作:sed -e 's/VARONE/NEWVALA/g' -e 's/VARTWO/NEWVALB/g' -e 's/VARTHR/NEWVALC/g' &lt; $TEMPLATE &gt; $OUTPUT
【解决方案10】:

如果您想使用Jinja2 模板,请参阅此项目:j2cli

它支持:

  • 来自 JSON、INI、YAML 文件和输入流的模板
  • 从环境变量模板化

【讨论】:

    【解决方案11】:

    已接受答案的更长但更强大的版本:

    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 不同,事实证明,它不这样做)。

    所以,如果你的环境是:

    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
    

    【讨论】:

      【解决方案12】:

      这是另一个纯 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>
      

      【讨论】:

        【解决方案13】:

        这是另一种解决方案:生成一个包含所有变量和模板文件内容的 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
        

        讨论

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

          ) | bash > output.txt
          

        【讨论】:

          【解决方案14】:

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

          【讨论】:

            【解决方案15】:

            如果使用 Perl 是一种选择,并且您满足于仅基于 环境 变量的扩展(而不是所有 shell 变量),考虑Stuart P. Bentley's robust answer

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

            目标是:

            • 支持扩展${name}$name 变量引用。
            • 阻止所有其他扩展:
              • 命令替换($(...) 和旧语法`...`
              • 算术替换($((...)) 和旧语法 $[...])。
            • 通过前缀\ (\${name}) 允许选择性抑制变量扩展。
            • 保留特殊字符。在输入中,特别是 "\ 实例。
            • 允许通过参数或标准输入进行输入。

            函数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(缺少关闭 })BREAK 函数。
            • 由于 bash 对双引号字符串的处理,反斜杠的处理方式如下:
              • \$name 阻止扩展。
              • 单个\ 后面没有$ 将按原样保留。
              • 如果要表示多个相邻的 \ 实例,则必须将它们加倍;例如。:
                • \\ -> \ - 与 \ 相同
                • \\\\ -> \\
              • 输入不得包含以下(很少使用)用于内部用途的字符:0x10x20x3
            • 在很大程度上假设的担忧是,如果 bash 应该引入新的扩展语法,此函数可能无法阻止此类扩展 - 请参阅下面的不使用 eval 的解决方案。

            如果您正在寻找支持${name}扩展的限制性更强的解决方案 - 即,使用强制花括号,忽略$name 参考资料 - 请参阅我的 this answer


            这里是accepted answer 提供的仅限 bash、eval-free 解决方案的改进版本

            改进如下:

            • 支持扩展${name}$name 变量引用。
            • 支持 \-escaping 不应扩展的变量引用。
            • 与上述基于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"
            

            【讨论】:

              【解决方案16】:

              shtpl 的完美案例。 (我的项目,所以它没有被广泛使用并且缺乏文档。但无论如何这是它提供的解决方案。你想测试它。)

              只需执行:

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

              结果是:

              the number is 1
              the word is dog
              

              玩得开心。

              【讨论】:

              • 如果是废话,无论如何都会被否决。我同意。但是好的,重点是,它不是很明显,它实际上是我的项目。将来会使其更加明显。无论如何,感谢您的评论和您的时间。
              • 我想补充一点,我昨天真的搜索了用例,其中 shtpl 将是一个完美的解决方案。是的,我很无聊......
              【解决方案17】:

              此页面描述了answer with awk

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

              【讨论】:

              • 这样可以保持所有引号不变。太好了!
              【解决方案18】:
              # 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 功能,可根据您的喜好进行调整,用于生产,不应因任何输入而中断。 如果它坏了 - 请告诉我。

              【讨论】:

                【解决方案19】:

                要跟进此页面上的 plockc's answer,这里有一个适合破折号的版本,适合那些希望避免 bashism 的人。

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

                【讨论】:

                  【解决方案20】:

                  您也可以使用 bashible(它在内部使用上面/下面描述的评估方法)。

                  有一个例子,如何从多个部分生成一个 HTML:

                  https://github.com/mig1984/bashible/tree/master/examples/templates

                  【讨论】:

                    【解决方案21】:

                    在这里查看简单的变量替换python脚本:https://github.com/jeckep/vsubst

                    使用非常简单:

                    python subst.py --props secure.properties --src_path ./templates --dst_path ./dist
                    

                    【讨论】:

                      【解决方案22】:

                      这是一个保留空格的 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}"
                      }
                      

                      【讨论】:

                        【解决方案23】:

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

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

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

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

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 2018-09-10
                          • 1970-01-01
                          • 1970-01-01
                          • 2019-11-24
                          • 1970-01-01
                          • 2012-02-04
                          • 1970-01-01
                          • 1970-01-01
                          相关资源
                          最近更新 更多