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

How to substitute shell variables in complex text files

提问人:Ben 提问时间:1/4/2013 最后编辑:Timur ShtatlandBen 更新时间:9/4/2023 访问量:196579

问:

我有几个文本文件,其中我引入了 shell 变量(例如$VAR 1 或 $VAR 2)。

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

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

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

这在非常基本的文件上效果很好。

但是在更复杂的文件上,“eval”命令的作用太多了:

  • 跳过以“#”开头的行

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

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

亲切问候

Linux Shell 字符串插值

评论


答:

0赞 w00t 1/4/2013 #1

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

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

1赞 w00t 1/4/2013 #2

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

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

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

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

评论

0赞 Ben 1/4/2013
事实上,在对可能包含错误代码的文件进行“评估”时,这就是风险
316赞 derobert 1/4/2013 #3

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

因此,这很容易:

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

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

评论

6赞 Ben 1/4/2013
伟大!但它仅适用于环境变量。如何使它与.sh脚本中声明的变量一起使用?
16赞 tlo 5/7/2014
@Ben:对要在 envsubst 中使用的每个变量使用“export”(在调用 envsubst 之前)
11赞 Andy 9/19/2014
envsubstGNU gettext 的一部分
3赞 derobert 11/3/2015
@user_mda输出的去向。
8赞 Kos 6/29/2018
警告:如果包含变量扩展之外的任何字符,也将替换这些字符,并且无法转义 .常见(丑陋的)解决方法是 。source.txt$envsubst$export DOLLAR="$"
96赞 Michael 1/22/2014 #4

关于答案 2,在讨论 envsubst 时,您问:

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

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

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

例如:

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

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

将分别替换 and (并且只有 和 ) 的所有实例。$VAR1$VAR2VAR1VAR2source.txt'somevalue''someothervalue'

评论

19赞 Mark Lakata 12/2/2015
用于设置的单引号至关重要'MYVARS
0赞 bishop 8/15/2017
请注意,当要替换的文本很大时,、 或任何交错命令都可能失败。参考。exportenvsubst
0赞 Thiago Figueiro 6/8/2018
@ram这是因为省略参数(即 )会导致所有导出的变量都被替换。如果这是您需要的,那么不用担心。SHELL_FORMAT"$MYVARS"
-1赞 Betlista 7/3/2017 #5

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"

也许它对某人有帮助。

0赞 Govind Kailas 9/15/2017 #6

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

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

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

14赞 Dado 1/16/2018 #7
  1. 定义 ENV 变量
$ export MY_ENV_VAR=congratulation
  1. 创建包含以下内容的模板文件 (in.txt
$MY_ENV_VAR

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

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

你应该看到“恭喜”。

33赞 inetphantom 2/13/2018 #8

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

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}

评论

2赞 Tremmillicious 6/11/2021
你救了我的命,秘密英雄,我不喜欢出口解决方案。谢谢
1赞 bitsmack 1/26/2022
多年后,仍然是最好的解决方案。谢谢!
2赞 Bohdan Shulha 2/7/2022
谢谢伙计,这个提示真的可以节省时间'$var1,$var3'
5赞 Arik 5/12/2020 #9

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

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

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

因此,如果有一个名为 的已定义的 env 变量,并且您的文件如下所示:$NAMEsource.txt

Hello $NAME
Your balance is 123 ($USD)

这将是:destination.txt

Hello Arik
Your balance is 123 ($USD)

请注意,替换了 并且保持不变$NAME$USD

0赞 Yordan Georgiev 8/17/2020 #10

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

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

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

4赞 man 2/17/2021 #11
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)

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

9赞 Radu Gabriel 12/6/2021 #12

还有这个选项:

在文件中定义变量

$ 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"
0赞 Borislav Markov 9/4/2023 #13

这是递归变量替换:

#!/bin/sh
find ./abc -type f | while read file; 
do  
   FF="$file";
   envsubst < ${FF} > ${FF}.replaced
   rm ${FF}
   mv ${FF}.replaced ${FF}
   echo "Replacing ${FF}"
done