104

我正在尝试研究如何使 bash (强制?)扩展字符串中的变量(从文件中加载)。

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

hello $FOO world

然后我跑

export FOO=42
echo $(cat something.txt)

这返回:

   hello $FOO world

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

有任何想法吗?

4

13 回答 13

129

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

envsubst < something.txt

示例:替换文件 source.txt 中的变量并将其写入destination.txt 以进行进一步处理

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

如果它在您的发行版中尚不可用,则它位于 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  "$@"
于 2015-08-10T18:13:41.730 回答
27

许多答案使用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"
}

例如,您可以将它与 a 一起使用,它parameters.cfg实际上是一个仅设置变量的 shell 脚本,而 atemplate.txt是使用这些变量的模板:

. parameters.cfg
printf "%s\n" "$(apply_shell_expansion template.txt)" > result.txt

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

于 2013-12-01T20:01:58.773 回答
26

你可以试试

echo $(eval echo $(cat something.txt))
于 2012-05-21T11:14:21.957 回答
8

您不想打印每一行,而是想对其进行评估,以便 Bash 可以执行变量替换。

FOO=42
while read; do
    eval echo "$REPLY"
done < something.txt

有关更多信息,请参阅help eval或 Bash 手册。

于 2012-05-21T10:23:10.967 回答
7

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

将 something.txt 的内容写入临时文件,并在其周围加上 echo 语句:

something=$(cat something.txt)

echo "echo \"" > temp.out
echo "$something" >> temp.out
echo "\"" >> temp.out

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

RESULT=$(source temp.out)

并且 $RESULT 将使其全部扩展。但似乎大错特错!


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

RESULT=$(source <(echo "echo \"$(cat something.txt)\""))
#or
RESULT=$(source <(echo "echo \"$(<something.txt)\""))
于 2012-05-22T02:27:50.870 回答
4

如果您只想扩展变量引用(我自己的目标),您可以执行以下操作。

contents="$(cat something.txt)"
echo $(eval echo \"$contents\")

($contents 周围的转义引号是这里的关键)

于 2015-08-13T20:25:11.003 回答
3
  1. 如果某事.txt只有一行,一个bash方法,(迈克尔尼尔“icky”答案的较短版本),使用过程命令替换

    FOO=42 . <(echo -e echo $(<something.txt))
    

    输出:

    hello 42 world
    

    请注意,这export不是必需的。

  2. 如果something.txt有一行或多行,GNU 评估方法:sed e

    FOO=42 sed 's/"/\\\"/g;s/.*/echo "&"/e' something.txt
    
于 2018-01-07T17:53:47.463 回答
1

以下解决方案:

  • 允许替换定义的变量

  • 保留未定义的变量占位符不变。这在自动部署期间特别有用。

  • 支持以下格式的变量替换:

    ${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
于 2017-03-16T18:09:49.113 回答
1
foo=45
file=something.txt       # in a file is written: Hello $foo world!
eval echo $(cat $file)
于 2018-09-11T15:51:12.967 回答
0
$ 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.
于 2018-07-27T20:21:29.513 回答
-1

envsubst如果您要替换的内容具有“合理”的长度,这是一个很好的解决方案(请参阅 LenW 的答案)。

就我而言,我需要替换文件的内容来替换变量名。envsubst要求将内容导出为环境变量,而bash在导出超过1兆字节左右的环境变量时会出现问题。

awk 解决方案

从另一个问题使用 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

该解决方案甚至适用于大文件。

于 2018-08-21T09:14:52.583 回答
-1
expenv () {
    LF=$'\n'
    echo "cat <<END_OF_TEXT${LF}$(< "$1")${LF}END_OF_TEXT" | bash
    return $?
}

expenv "file name"
于 2021-02-16T14:16:43.770 回答
-3

以下作品:bash -c "echo \"$(cat something.txt)"\"

于 2018-04-17T11:35:09.187 回答