我试图找出如何使bash(强制?)扩展字符串(从文件加载)中的变量。
我有一个名为“ something.txt”的文件,其内容如下:
hello $FOO world
然后我跑
export FOO=42
echo $(cat something.txt)
这将返回:
hello $FOO world
即使设置了变量,它也不会扩展$ FOO。我无法评估或获取文件-因为它将尝试执行该文件(它本身不能执行-我只希望插入包含变量的字符串)。
有任何想法吗?
我试图找出如何使bash(强制?)扩展字符串(从文件加载)中的变量。
我有一个名为“ something.txt”的文件,其内容如下:
hello $FOO world
然后我跑
export FOO=42
echo $(cat something.txt)
这将返回:
hello $FOO world
即使设置了变量,它也不会扩展$ FOO。我无法评估或获取文件-因为它将尝试执行该文件(它本身不能执行-我只希望插入包含变量的字符串)。
有任何想法吗?
eval
务必要注意其中的含义。
Answers:
我偶然发现了我认为该问题的答案: envsubst
命令。
envsubst < something.txt
如果您的发行版中尚未提供该功能,请在
gettext
。@Rockallite-我写了一些包装脚本来解决'\ $'问题。
(顺便说一句,envsubst有一个“功能”,在https://unix.stackexchange.com/a/294400/7088上 进行了解释, 该扩展仅用于扩展输入中的某些变量,但我同意转义例外的内容要多得多方便。)
这是我的脚本:
#! /bin/bash
## -*-Shell-Script-*-
CmdName=${0##*/}
Usage="usage: $CmdName runs envsubst, but allows '\$' to keep variables from
being expanded.
With option -sl '\$' keeps the back-slash.
Default is to replace '\$' with '$'
"
if [[ $1 = -h ]] ;then echo -e >&2 "$Usage" ; exit 1 ;fi
if [[ $1 = -sl ]] ;then sl='\' ; shift ;fi
sed 's/\\\$/\${EnVsUbDolR}/g' | EnVsUbDolR=$sl\$ envsubst "$@"
$
)的转义,例如,echo '\$USER' | envsubst
将输出带有前缀反斜杠not的当前用户名$USER
。
brew link --force gettext
许多答案的使用eval
和echo
工作方式都是合理的,但是在各种事情上都存在问题,例如多行代码,试图转义外壳元字符,不打算通过bash进行扩展的模板内的逃逸等。
我遇到了同样的问题,并编写了这个shell函数,据我所知,它可以正确处理所有内容。由于bash的命令替换规则,这仍将仅删除模板中的尾随换行符,但是只要其他所有内容保持不变,我就永远不会发现这是一个问题。
apply_shell_expansion() {
declare file="$1"
declare data=$(< "$file")
declare delimiter="__apply_shell_expansion_delimiter__"
declare command="cat <<$delimiter"$'\n'"$data"$'\n'"$delimiter"
eval "$command"
}
例如,您可以像这样使用它,aparameters.cfg
实际上是只设置变量的shell脚本,atemplate.txt
是使用这些变量的模板:
. parameters.cfg
printf "%s\n" "$(apply_shell_expansion template.txt)" > result.txt
在实践中,我将其用作一种轻量级的模板系统。
eval
陷阱。尝试; $(eval date)
在输入文件中任何行的末尾添加,它将运行date
命令。
以下解决方法:
允许替换定义的变量
保留未定义的变量占位符不变。这在自动部署期间特别有用。
支持以下格式的变量替换:
${var_NAME}
$var_NAME
报告在环境中未定义的变量,并在这种情况下返回错误代码
TARGET_FILE=someFile.txt;
ERR_CNT=0;
for VARNAME in $(grep -P -o -e '\$[\{]?(\w+)*[\}]?' ${TARGET_FILE} | sort -u); do
VAR_VALUE=${!VARNAME};
VARNAME2=$(echo $VARNAME| sed -e 's|^\${||g' -e 's|}$||g' -e 's|^\$||g' );
VAR_VALUE2=${!VARNAME2};
if [ "xxx" = "xxx$VAR_VALUE2" ]; then
echo "$VARNAME is undefined ";
ERR_CNT=$((ERR_CNT+1));
else
echo "replacing $VARNAME with $VAR_VALUE2" ;
sed -i "s|$VARNAME|$VAR_VALUE2|g" ${TARGET_FILE};
fi
done
if [ ${ERR_CNT} -gt 0 ]; then
echo "Found $ERR_CNT undefined environment variables";
exit 1
fi
如果something.txt只有一个线,bash
方法,(短版的迈克尔·尼尔的“恶心”的答案),使用过程和命令替换:
FOO=42 . <(echo -e echo $(<something.txt))
输出:
hello 42 world
请注意,这export
不是必需的。
如果something.txt有一行或多行,则使用GNU valuate方法:sed
e
FOO=42 sed 's/"/\\\"/g;s/.*/echo "&"/e' something.txt
sed
评估最适合我的目的。我需要从模板生成脚本,这些模板的值将从另一个配置文件中导出的变量填充。它可以正确地转义命令扩展,例如放入\$(date)
模板以获取$(date)
输出。谢谢!
$ eval echo $(cat something.txt)
hello 42 world
$ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17)
Copyright (C) 2007 Free Software Foundation, Inc.
envsubst
如果您要替换的内容长度“合理”,则是一个很好的解决方案(请参阅LenW的答案)。
就我而言,我需要替换文件的内容来替换变量名。envsubst
要求将内容作为环境变量导出,而bash在导出大于一兆字节左右的环境变量时会出现问题。
使用来自另一个问题的cuonglm解决方案:
needle="doc1_base64" # The "variable name" in the file. (A $ is not needed.)
needle_file="doc1_base64.txt" # Will be substituted for the needle
haystack=$requestfile1 # File containing the needle
out=$requestfile2
awk "BEGIN{getline l < \"${needle_file}\"}/${needle}/{gsub(\"${needle}\",l)}1" $haystack > $out
该解决方案甚至适用于大型文件。
eval
。