【问题标题】:How to substitute shell variables in complex text files如何替换复杂文本文件中的 shell 变量
【发布时间】:2012-12-18 19:12:27
【问题描述】:

我有几个文本文件,我在其中引入了 shell 变量(例如 $VAR1 或 $VAR2)。

我想把这些文件(一个一个)保存到所有变量都被替换的新文件中。

为此,我使用了以下 shell 脚本(可在 StackOverflow 上找到):

while read line
do
    eval echo "$line" >> destination.txt
done < "source.txt"

这对非常基本的文件非常有效。

但是对于更复杂的文件,“eval”命令做的太多了:

  • 跳过以“#”开头的行

  • XML 文件解析导致大量错误

有没有更好的方法呢? (在 shell 脚本中......我知道这很容易用 Ant 完成)

亲切的问候

【问题讨论】:

    标签: linux shell string-interpolation


    【解决方案1】:

    看,原来在我的系统上有一个envsubst 命令,它是gettext-base 包的一部分。

    所以,这很容易:

    envsubst < "source.txt" > "destination.txt"
    

    请注意,如果您想对两者使用相同的文件,则必须使用类似 moreutil 的 sponge,正如 Johnny Utahh 所建议的那样:envsubst &lt; "source.txt" | sponge "source.txt"。 (因为 shell 重定向会在读取之前清空文件。)

    【讨论】:

    • 太棒了!但它只适用于环境变量。如何使它与在我的 .sh 脚本中声明的变量一起工作?
    • @Ben: 对你想在 envsubst 中使用的每个变量使用“export”(在调用 envsubst 之前)
    • envsubstGNU gettext 的一部分
    • @user_mda 输出到哪里。
    • 警告:如果source.txt 包含变量扩展之外的任何$ 字符,envsubst 也将替换这些字符,并且无法转义$。常见(丑陋)的解决方法是export DOLLAR="$"
    【解决方案2】:

    参考答案 2,在讨论 envsubst 时,您问:

    如何使它与在我的 .sh 脚本中声明的变量一起工作?

    答案是您只需在调用envsubst 之前导出变量。

    您还可以使用envsubst SHELL_FORMAT 参数限制要在输入中替换的变量字符串(避免意外替换输入中的字符串为常见的shell 变量值——例如$HOME)。

    例如:

    export VAR1='somevalue' VAR2='someothervalue'
    MYVARS='$VAR1:$VAR2'
    
    envsubst "$MYVARS" <source.txt >destination.txt
    

    source.txt 中的所有$VAR1$VAR2 实例(并且仅VAR1VAR2)分别替换为'somevalue''someothervalue'

    【讨论】:

    • 用来设置MYVARS的单引号'很关键
    • 请注意,exportenvsubst 或任何交错命令可能会在 text-to-substitute 很大时失败。 Reference.
    • @ram 这是因为省略 SHELL_FORMAT 参数(即"$MYVARS")会导致所有导出的变量都被替换。如果这是您需要的,那就不用担心了。
    【解决方案3】:

    我知道这个话题很老,但我有一个更简单的工作解决方案,无需导出变量。可以是oneliner,但我更喜欢在行尾使用\进行拆分。

    var1='myVar1'\
    var2=2\
    var3=${var1}\
    envsubst '$var1,$var3' < "source.txt" > "destination.txt"
    
    #         ^^^^^^^^^^^    ^^^^^^^^^^     ^^^^^^^^^^^^^^^
    # define which to replace   input            output
    

    需要将变量定义到与envsubst 相同的行才能被视为环境变量。

    '$var1,$var3' 是可选的,仅替换指定的。想象一个包含不应被替换的${VARIABLE_USED_BY_JENKINS} 的输入文件。

    【讨论】:

    • 你救了我的命,秘密英雄,我不喜欢导出解决方案。谢谢
    • 多年后,仍然是最好的解决方案。谢谢!
    【解决方案4】:
    1. 定义您的 ENV 变量
    $ export MY_ENV_VAR=congratulation
    
    1. 使用以下内容创建模板文件 (in.txt)
    $MY_ENV_VAR
    

    您还可以使用系统定义的所有其他 ENV 变量,例如(在 linux 中)$TERM、$SHELL、$HOME...

    1. 运行此命令将所有环境变量替换为 in.txt 文件并将结果写入 out.txt
    $ envsubst "`printf '${%s} ' $(sh -c "env|cut -d'=' -f1")`" < in.txt > out.txt
    
    1. 检查 out.txt 文件的内容
    $ cat out.txt
    

    你应该看到“祝贺”。

    【讨论】:

      【解决方案5】:

      如果您希望在源文件中替换环境变量,同时保留所有非环境变量,您可以使用以下命令:

      envsubst "$(printf '${%s} ' $(env | sed 's/=.*//'))" < source.txt > destination.txt
      

      仅替换特定变量的语法是explained here。上面的命令是使用子shell列出所有定义的变量,然后将其传递给envsubst

      因此,如果定义了一个名为 $NAME 的环境变量,并且您的 source.txt 文件如下所示:

      Hello $NAME
      Your balance is 123 ($USD)
      

      destination.txt 将是:

      Hello Arik
      Your balance is 123 ($USD)
      

      注意$NAME 被替换,$USD 保持不变

      【讨论】:

        【解决方案6】:

        还有这个选项:

        在文件中定义变量

        $ cat variables.env 
        # info about what this var is
        export var1=a
        # info about var again
        export var2=b
        

        定义一个使用变量的模板文件

        $ cat file1-template.txt
        This is var1: "${var1}"
        This is var2: "${var2}"
        

        生成最终文件,将变量替换为值

        $ source variables.env
        $ envsubst < file1-template.txt > file1.txt
        $ cat file1.txt 
        This is var1: "a"
        This is var2: "b"
        

        【讨论】:

          【解决方案7】:
          while IFS='=' read -r name value ; do
              # Print line if found variable
              sed -n '/${'"${name}"'}/p' docker-compose.yml
              # Replace variable with value. 
              sed -i 's|${'"${name}"'}|'"${value}"'|' docker-compose.yml
          done < <(env)
          

          注意:变量名称或值不应包含“|”,因为它用作分隔符。

          【讨论】:

            【解决方案8】:

            如果您真的只想使用 bash(和 sed),那么我将检查您的每个环境变量(由 set 在 posix 模式下返回)并从中构建一堆 -e 'regex' 用于 sed,由 -e 's/\$[a-zA-Z_][a-zA-Z0-9_]*//g' 终止,然后将所有内容传递给 sed。

            不过,Perl 会做得更好,您可以将环境变量作为数组访问,并且可以执行可执行替换,因此您只需匹配任何环境变量一次。

            【讨论】:

              【解决方案9】:

              实际上,您需要将read 更改为read -r,这将使其忽略反斜杠。

              此外,您应该转义引号和反斜杠。 所以

              while read -r line; do
                line="${line//\\/\\\\}"
                line="${line//\"/\\\"}"
                line="${line//\`/\\\`}"
                eval echo "\"$line\""
              done > destination.txt < source.txt
              

              不过,这仍然是一种糟糕的扩展方式。

              【讨论】:

              • 确实,这就是对可能包含错误代码的文件执行“评估”时的风险
              【解决方案10】:

              导出所有需要的变量,然后使用 perl onliner

              TEXT=$(echo "$TEXT"|perl -wpne 's#\${?(\w+)}?# $ENV{$1} // $&amp; #ge;')

              这会将 TEXT 中存在的所有 ENV 变量替换为实际值。 引号也被保留了:)

              【讨论】:

                【解决方案11】:

                通过在单引号中运行 perl 代码 (-e) 来调用 perl 二进制文件,在每行模式下搜索和替换 (-pi),该代码遍历包含的特殊 %ENV 哈希的键导出的变量名称作为键,导出的变量值作为键的值,对于每次迭代,只需将包含$&lt;&lt;key&gt;&gt; 的字符串替换为其&lt;&lt;value&gt;&gt;

                 perl -pi -e 'foreach $key(sort keys %ENV){ s/\$$key/$ENV{$key}/g}' file
                

                警告: 对于两个或多个变量以相同字符串开头的情况,需要额外的逻辑处理...

                【讨论】:

                  【解决方案12】:

                  envsubst 看起来和我想用的完全一样,但是-v 选项让我有点吃惊。

                  虽然envsubst &lt; template.txt 工作正常,但选项-v 无法正常工作:

                  $ cat /etc/redhat-release
                  Red Hat Enterprise Linux Server release 7.1 (Maipo)
                  $ envsubst -V
                  envsubst (GNU gettext-runtime) 0.18.2
                  Copyright (C) 2003-2007 Free Software Foundation, Inc.
                  License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
                  This is free software: you are free to change and redistribute it.
                  There is NO WARRANTY, to the extent permitted by law.
                  Written by Bruno Haible.
                  

                  正如我所写,这不起作用:

                  $ envsubst -v < template.txt
                  envsubst: missing arguments
                  $ cat template.txt | envsubst -v
                  envsubst: missing arguments
                  

                  我必须这样做才能使它工作:

                  TEXT=`cat template.txt`; envsubst -v "$TEXT"
                  

                  也许它对某人有帮助。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2022-12-18
                    • 1970-01-01
                    • 2014-12-06
                    • 2019-04-19
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多