如何在复杂的文本文件中替换 shell 变量

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

我想采取这些文件(一个接一个) ,并保存在新的文件中的所有变量将被替换。

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

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

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

但对于更复杂的文件,“ eval”命令的作用太大:

  • 跳过以“ #”开头的行

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

还有更好的办法吗?(在 shell 脚本中... 我知道这很容易用 Ant 实现)

问候你

175919 次浏览

如果您真的只想使用 bash (和 sed) ,那么我将遍历您的每个环境变量(在 posx 模式下由 set返回) ,并从中为 sed 构建一组 -e 'regex',以 -e 's/\$[a-zA-Z_][a-zA-Z0-9_]*//g'结束,然后将所有这些传递给 sed。

Perl 会做得更好,你可以以数组的形式访问环境 vars,你可以做可执行的替换,所以你只能匹配任何一个环境变量。

实际上你需要改变你的 readread -r,这将使它忽略反斜杠。

此外,您应该转义引号和反斜杠。 那么

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

尽管如此,这种扩张方式还是很糟糕。

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

因此,这让事情变得简单了:

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

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

关于答案2,当讨论 envsubst 时,你问:

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

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

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

例如:

export VAR1='somevalue' VAR2='someothervalue'
MYVARS='$VAR1:$VAR2'


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

将用 'somevalue''someothervalue'分别代替 source.txt中的所有 $VAR1$VAR2实例(以及只有 VAR1VAR2)。

envsubst看起来正是我想要使用的东西,但是 -v选项让我有点吃惊。

尽管 envsubst < 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"

也许能帮到别人。

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

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

这将用实际值替换 TEXT 中出现的所有 ENV 变量。 报价也被保留:)

  1. 定义 ENV 变量
$ export MY_ENV_VAR=congratulation
  1. 创建包含以下内容的模板文件(In.txt)
$MY_ENV_VAR

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

  1. 运行这个命令来回溯 In.txt文件中的所有 env 变量,并将结果写入 输出
$ envsubst "`printf '${%s} ' $(sh -c "env|cut -d'=' -f1")`" < in.txt > out.txt
  1. 检查 out.txt 文件的内容
$ cat out.txt

你应该看看“恭喜”。

我知道这个主题已经过时了,但是我有一个更简单的工作解决方案,不需要导出变量。可以是一线,但我更喜欢分裂使用 \在线结束。

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}的输入文件,这个文件不应该被替换。

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

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

只替换特定变量的语法是 在这里解释。上面的命令使用子 shell 列出所有定义的变量,然后将其传递给 envsubst

因此,如果有一个定义好的 env 变量 $NAME,那么您的 source.txt文件如下所示:

Hello $NAME
Your balance is 123 ($USD)

destination.txt将包括:

Hello Arik
Your balance is 123 ($USD)

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

调用 perl 二进制文件,在每行模式(-pi)中搜索并通过在单引号中运行 perl 代码(-e)替换 perl 二进制文件。单引号迭代特殊 %ENV散列的键,该散列包含导出的变量名作为键,导出的变量值作为键的值,每次迭代只需用 <<value>>替换一个包含 $<<key>>的字符串。

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

注意: 对于两个或多个 var 以同一个字符串开头的情况,需要进行额外的逻辑处理..。

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)

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

还有一个选择:

在文件中定义变量

$ 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"