提问人:Ben 提问时间:1/4/2013 最后编辑:Timur ShtatlandBen 更新时间:9/4/2023 访问量:196579
如何替换复杂文本文件中的 shell 变量
How to substitute shell variables in complex text files
问:
我有几个文本文件,其中我引入了 shell 变量(例如$VAR 1 或 $VAR 2)。
我想把这些文件(一个接一个)保存在新文件中,所有变量都会被替换。
为此,我使用了以下 shell 脚本(可在 StackOverflow 上找到):
while read line
do
eval echo "$line" >> destination.txt
done < "source.txt"
这在非常基本的文件上效果很好。
但是在更复杂的文件上,“eval”命令的作用太多了:
跳过以“#”开头的行
XML 文件解析会导致大量错误
有没有更好的方法?(在 shell 脚本中...例如,我知道这很容易用 Ant 完成)
亲切问候
答:
如果你真的只想使用 bash(和 sed),那么我会遍历你的每个环境变量(在 posix 模式下返回),并从中构建一堆 for sed,以 a 结尾,然后将所有这些传递给 sed。set
-e 'regex'
-e 's/\$[a-zA-Z_][a-zA-Z0-9_]*//g'
不过,Perl 会做得更好,您可以访问环境变量作为数组,并且您可以进行可执行的替换,因此您只匹配一次任何环境变量。
实际上,您需要将其更改为这将使其忽略反斜杠。read
read -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 的东西,正如 Johnny Utahh 所建议的那样: .(因为 shell 重定向将在读取文件之前清空文件。sponge
envsubst < "source.txt" | sponge "source.txt"
评论
source.txt
$
envsubst
$
export DOLLAR="$"
关于答案 2,在讨论 envsubst 时,您问:
如何使它与.sh脚本中声明的变量一起使用?
答案是你只需要在调用之前导出你的变量。envsubst
您还可以使用参数限制要在输入中替换的变量字符串(避免使用通用 shell 变量值意外替换输入中的字符串 - 例如 )。envsubst
SHELL_FORMAT
$HOME
例如:
export VAR1='somevalue' VAR2='someothervalue'
MYVARS='$VAR1:$VAR2'
envsubst "$MYVARS" <source.txt >destination.txt
将分别替换 and (并且只有 和 ) 的所有实例。$VAR1
$VAR2
VAR1
VAR2
source.txt
'somevalue'
'someothervalue'
评论
'
MYVARS
SHELL_FORMAT
"$MYVARS"
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 onliner
TEXT=$(echo "$TEXT"|perl -wpne 's#\${?(\w+)}?# $ENV{$1} // $& #ge;')
这会将 TEXT 中存在的所有 ENV 变量替换为实际值。 引号也被保留:)
- 定义 ENV 变量
$ export MY_ENV_VAR=congratulation
- 创建包含以下内容的模板文件 (in.txt)
$MY_ENV_VAR
您还可以使用系统定义的所有其他 ENV 变量,例如(在 linux 中)$TERM、$SHELL、$HOME...
- 运行此命令以将所有 env-variables 放入 in.txt 文件中,并将结果写入 out.txt
$ envsubst "`printf '${%s} ' $(sh -c "env|cut -d'=' -f1")`" < in.txt > out.txt
- 检查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}
评论
'$var1,$var3'
如果要替换源文件中的 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 二进制文件,在搜索和替换每行模式 ( the ) 中,通过在单引号中运行 perl 代码 ( the ),该代码遍历包含导出的变量名称作为键和导出的变量值作为键值的特殊哈希的键,并且对于每次迭代,只需将包含 a 的字符串替换为其 .-pi
-e
%ENV
$<<key>>
<<value>>
perl -pi -e 'foreach $key(sort keys %ENV){ s/\$$key/$ENV{$key}/g}' file
警告: 如果两个或多个变量以相同的字符串开头,则需要额外的逻辑处理......
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"
这是递归变量替换:
#!/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
评论