强制bash扩展从文件加载的字符串中的变量

Mic*_*ale 75 unix linux bash shell

我试图找出如何使字符串(从文件加载)中的bash(强制?)扩展变量.

我有一个名为"something.txt"的文件,内容如下:

hello $FOO world
Run Code Online (Sandbox Code Playgroud)

然后我跑了

export FOO=42
echo $(cat something.txt)
Run Code Online (Sandbox Code Playgroud)

这会返回:

   hello $FOO world
Run Code Online (Sandbox Code Playgroud)

即使变量已设置,它也没有扩展$ FOO.我无法评估或获取文件 - 因为它会尝试并执行它(它不是可执行的 - 我只是想要插入变量的字符串).

有任何想法吗?

Len*_*enW 93

我偶然发现了我认为这个问题的答案: envsubst命令.

envsubst < something.txt
Run Code Online (Sandbox Code Playgroud)

如果你的发行版中还没有它,它就在你的发行版中

GNU包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  "$@"
Run Code Online (Sandbox Code Playgroud)

  • +1.这是唯一的答案是_guaranteed_不做命令替换,引用删除,参数处理等. (5认同)
  • 它只适用于完全导出的shell变量(在bash中).例如S ='$ a $ b'; A =集; 出口b =出口; echo $ S | envsubst; 产量:"出口" (5认同)
  • 似乎是在家用自制需要`brew link --force gettext`的mac上 (3认同)

wjl*_*wjl 26

许多答案使用evalecho工作,但打破各种事情,如多行,试图逃避shell元字符,逃避模板内部不打算通过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"
}
Run Code Online (Sandbox Code Playgroud)

例如,您可以像这样使用它,parameters.cfg它实际上是一个只设置变量的shell脚本,以及一个template.txt使用这些变量的模板:

. parameters.cfg
printf "%s\n" "$(apply_shell_expansion template.txt)" > result.txt
Run Code Online (Sandbox Code Playgroud)

在实践中,我将其用作一种轻量级模板系统.

  • 这是我到目前为止发现的最好的技巧之一:).根据你的答案,可以很容易地构建一个函数,用一个包含对其他变量的引用的字符串来扩展变量 - 只需在函数中用$ 1替换$ data,我们就去了!我相信这是对原始问题BTW的最佳答案. (4认同)
  • @anubhava 我不确定你的意思;这实际上是本例中所需的扩展行为。换句话说,是的,您应该能够用它执行任意 shell 代码。 (3认同)

piz*_*zza 18

你可以试试

echo $(eval echo $(cat something.txt))
Run Code Online (Sandbox Code Playgroud)

  • 如果需要保留新行,请使用`echo -e"$(eval"echo -e \"\`<something.txt \`\"")"` (6认同)
  • 但前提是你的“template.txt”是文本。**此操作很危险,因为它会执行(评估)命令(如果有)。** (2认同)
  • 但这删除了双引号 (2认同)

Tod*_*obs 10

你不想打印每一行,你想要评估它,以便Bash可以执行变量替换.

FOO=42
while read; do
    eval echo "$REPLY"
done < something.txt
Run Code Online (Sandbox Code Playgroud)

有关help eval详细信息,请参阅或Bash手册.

  • [`eval` 很危险](http://mywiki.wooledge.org/BashFAQ/048); 您不仅会扩展 `$varname`(就像使用 `envsubst` 一样),还会扩展 `$(rm -rf ~)`。 (2认同)

Mic*_*ale 5

另一种方法(看起来很恶心,但我还是把它放在这里):

将 something.txt 的内容写入临时文件,并用 echo 语句包裹它:

something=$(cat something.txt)

echo "echo \"" > temp.out
echo "$something" >> temp.out
echo "\"" >> temp.out
Run Code Online (Sandbox Code Playgroud)

然后将其重新输入到变量中:

RESULT=$(source temp.out)
Run Code Online (Sandbox Code Playgroud)

并且 $RESULT 将全部扩展。但好像大错特错!


不需要临时文件的单行解决方案:

RESULT=$(source <(echo "echo \"$(cat something.txt)\""))
#or
RESULT=$(source <(echo "echo \"$(<something.txt)\""))
Run Code Online (Sandbox Code Playgroud)