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


84

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

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

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

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

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

但是在更复杂的文件上,“ eval”命令执行的操作过多:

  • 跳过以“#”开头的行

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

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

亲切的问候

Answers:


196

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

因此,这很容易:

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

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


4
大!但这仅适用于环境变量。如何使其与.sh脚本中声明的变量一起使用?

10
@Ben:对于要在envsubst中使用的每个变量使用“导出”(在调用envsubst之前)
tlo

7
envsubstGNU gettext的
Andy

3
@user_mda输出到达的位置。
derobert

4
警告:如果变量扩展之外source.txt包含任何$字符,envsubst也将替换这些字符,并且无法转义$。常见的(丑陋的)解决方法是export DOLLAR="$"
科斯

60

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

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

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

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

例如:

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

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

将分别用和替换in$VAR1$VAR2(且仅VAR1and VAR2)中的所有实例。source.txt'somevalue''someothervalue'


14
单引号'是用来设定MYVARS是至关重要的
马克Lakata

需要注意的是exportenvsubst时,或文本-取而代之的是大量任何交错命令可能会失败。参考。
主教

@ram是因为省略SHELL_FORMAT参数(即"$MYVARS")会导致所有导出的变量被替换。如果那是您的需要,那就不用担心了。
Thiago Figueiro

12

我知道这个主题很旧,但是我有一个更简单的工作解决方案,无需导出变量。可以是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}不应替换的输入文件。


8
  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

您应该会看到“祝贺”。


0

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

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


为什么有人会拒绝这样的答案?-perl -pi -e 'foreach $key(sort keys %ENV){ s/\$$key/$ENV{$key}/g}' "$main_tf_file"
约尔丹Georgiev的

0

实际上,您需要将其更改readread -r忽略反斜杠的位置。

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

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

不过,这仍然是扩展的可怕方法。


事实上,这对可能包含恶意代码的文件做“EVAL”时的风险

0

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

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

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


0

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

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

此处解释替换特定变量的语法。上面的命令使用子外壳列出所有定义的变量,然后将其传递给envsubst

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

Hello $NAME
Your balance is 123 ($USD)

destination.txt会是:

Hello Arik
Your balance is 123 ($USD)

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


0

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

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

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


-1

envsubst似乎完全像我想要使用的东西,但是-v选择让我有些惊讶。

虽然envsubst < template.txt工作正常,但与option相同-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"

也许对某人有帮助。

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.