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

时间:2022-02-10 10:11:21

I am trying to work out how to make bash (force?) expand variables in a string (which was loaded from a file).

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

I have a file called "something.txt" with the contents:

我有一个文件叫“某物”。txt”内容:

hello $FOO world

I then run

然后我跑

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

this returns:

这将返回:

   hello $FOO world

It didn't expand $FOO even though the variable was set. I can't eval or source the file - as it will try and execute it (it isn't executable as it is - I just want the string with the variables interpolated).

它没有扩展$FOO,尽管变量被设置了,我不能eval或源文件,因为它将尝试并执行它(它不是可执行的,因为它是-我只想要有变量插入的字符串)。

Any ideas?

什么好主意吗?

9 个解决方案

#1


56  

I stumbled on what I think is THE answer to this question: the envsubst command.

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

envsubst < something.txt

In case it's not already available in your distro, it's in the

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

GNU package gettext.

@Rockallite - I wrote a little wrapper script to take care of the '\$' problem.

@Rockallite——我写了一个小的包装脚本来解决“\$”问题。

(BTW, there is a "feature" of envsubst, explained at https://unix.stackexchange.com/a/294400/7088 for expanding only some of the variables in the input, but I agree that escaping the exceptions is much more convenient.)

(顺便说一句,envsubst有一个“特性”,在https://unix.stackexchange.com/a/294400/7088中解释为只扩展输入中的一些变量,但我同意,避免异常要方便得多。)

Here's my script:

这是我的脚本:

#! /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  "$@"

#2


24  

Many of the answers using eval and echo kind of work, but break on various things, such as multiple lines, attempting to escaping shell meta-characters, escapes inside the template not intended to be expanded by bash, etc.

许多答案都使用eval和echo之类的方法,但在不同的方面(如多行、试图转义shell元字符、在不想被bash扩展的模板中转义等等)上都出现了问题。

I had the same issue, and wrote this shell function, which as far as I can tell, handles everything correctly. This will still strip only trailing newlines from the template, because of bash's command substitution rules, but I've never found that to be an issue as long as everything else remains intact.

我也遇到了同样的问题,我写了这个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"
}

For example, you can use it like this with a parameters.cfg which is really a shell script that just sets variables, and a template.txt which is a template that uses those variables:

例如,您可以像这样对参数使用它。cfg实际上是一个shell脚本,它只设置变量,还有一个模板。txt是一个使用这些变量的模板:

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

In practice, I use this as a sort of lightweight template system.

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

#3


15  

you can try

你可以试着

echo $(eval echo $(cat something.txt))

#4


10  

You don't want to print each line, you want to evaluate it so that Bash can perform variable substitutions.

您不希望打印每一行,您希望对其进行计算,以便Bash能够执行变量替换。

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

See help eval or the Bash manual for more information.

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

#5


3  

Another approach (which seems icky, but I am putting it here anyway):

另一种方法(似乎有点恶心,但我还是把它放在这里):

Write the contents of something.txt to a temp file, with an echo statement wrapped around it:

写出某事的内容。txt到一个临时文件,用一个echo语句包围它:

something=$(cat something.txt)

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

then source it back in to a variable:

然后把它重新输入到一个变量中:

RESULT=$(source temp.out)

and the $RESULT will have it all expanded. But it seems so wrong !

而美元的结果将使它得到扩展。但这似乎是错的!

#6


1  

If you only want the variable references to be expanded (an objective that I had for myself) you could do the below.

如果您只想要扩展的变量引用(我为自己设置的一个目标),您可以在下面做。

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

(The escaped quotes around $contents is key here)

(在这里,围绕$contents的转义引号是关键)

#7


0  

Following solution:

以下解决方案:

  • allows replacing of variables which are defined

    允许替换已定义的变量

  • leaves unchanged variables placeholders which are not defined. This is especially useful during automated deployments.

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

  • supports replacement of variables in following formats:

    支持以下列格式替换变量:

    ${var_NAME}

    $ { var_NAME }

    $var_NAME

    var_NAME美元

  • reports which variables are not defined in environment and returns error code for such cases

    报告在环境中未定义的变量,并为此类情况返回错误代码



    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

#8


0  

  1. bash method, (one-line variant of Michael Neale's answer), using process & command substitution:

    bash方法(Michael Neale的答案的单行变体),使用过程和命令替换:

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

    Output:

    输出:

    hello 42 world
    

    Note that export isn't needed.

    注意,不需要导出。

  2. GNU sed evaluate method, if something.txt has many lines:

    GNU sed evaluate方法。三种有许多行:

    FOO=42 sed 's/"/\\\"/g;s/.*/echo "&"/e' something.txt
    

#9


-1  

The following works: bash -c "echo \"$(cat something.txt)"\"

以下工作:bash -c“echo”$(cat something.txt)“\”

#1


56  

I stumbled on what I think is THE answer to this question: the envsubst command.

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

envsubst < something.txt

In case it's not already available in your distro, it's in the

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

GNU package gettext.

@Rockallite - I wrote a little wrapper script to take care of the '\$' problem.

@Rockallite——我写了一个小的包装脚本来解决“\$”问题。

(BTW, there is a "feature" of envsubst, explained at https://unix.stackexchange.com/a/294400/7088 for expanding only some of the variables in the input, but I agree that escaping the exceptions is much more convenient.)

(顺便说一句,envsubst有一个“特性”,在https://unix.stackexchange.com/a/294400/7088中解释为只扩展输入中的一些变量,但我同意,避免异常要方便得多。)

Here's my script:

这是我的脚本:

#! /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  "$@"

#2


24  

Many of the answers using eval and echo kind of work, but break on various things, such as multiple lines, attempting to escaping shell meta-characters, escapes inside the template not intended to be expanded by bash, etc.

许多答案都使用eval和echo之类的方法,但在不同的方面(如多行、试图转义shell元字符、在不想被bash扩展的模板中转义等等)上都出现了问题。

I had the same issue, and wrote this shell function, which as far as I can tell, handles everything correctly. This will still strip only trailing newlines from the template, because of bash's command substitution rules, but I've never found that to be an issue as long as everything else remains intact.

我也遇到了同样的问题,我写了这个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"
}

For example, you can use it like this with a parameters.cfg which is really a shell script that just sets variables, and a template.txt which is a template that uses those variables:

例如,您可以像这样对参数使用它。cfg实际上是一个shell脚本,它只设置变量,还有一个模板。txt是一个使用这些变量的模板:

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

In practice, I use this as a sort of lightweight template system.

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

#3


15  

you can try

你可以试着

echo $(eval echo $(cat something.txt))

#4


10  

You don't want to print each line, you want to evaluate it so that Bash can perform variable substitutions.

您不希望打印每一行,您希望对其进行计算,以便Bash能够执行变量替换。

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

See help eval or the Bash manual for more information.

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

#5


3  

Another approach (which seems icky, but I am putting it here anyway):

另一种方法(似乎有点恶心,但我还是把它放在这里):

Write the contents of something.txt to a temp file, with an echo statement wrapped around it:

写出某事的内容。txt到一个临时文件,用一个echo语句包围它:

something=$(cat something.txt)

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

then source it back in to a variable:

然后把它重新输入到一个变量中:

RESULT=$(source temp.out)

and the $RESULT will have it all expanded. But it seems so wrong !

而美元的结果将使它得到扩展。但这似乎是错的!

#6


1  

If you only want the variable references to be expanded (an objective that I had for myself) you could do the below.

如果您只想要扩展的变量引用(我为自己设置的一个目标),您可以在下面做。

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

(The escaped quotes around $contents is key here)

(在这里,围绕$contents的转义引号是关键)

#7


0  

Following solution:

以下解决方案:

  • allows replacing of variables which are defined

    允许替换已定义的变量

  • leaves unchanged variables placeholders which are not defined. This is especially useful during automated deployments.

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

  • supports replacement of variables in following formats:

    支持以下列格式替换变量:

    ${var_NAME}

    $ { var_NAME }

    $var_NAME

    var_NAME美元

  • reports which variables are not defined in environment and returns error code for such cases

    报告在环境中未定义的变量,并为此类情况返回错误代码



    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

#8


0  

  1. bash method, (one-line variant of Michael Neale's answer), using process & command substitution:

    bash方法(Michael Neale的答案的单行变体),使用过程和命令替换:

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

    Output:

    输出:

    hello 42 world
    

    Note that export isn't needed.

    注意,不需要导出。

  2. GNU sed evaluate method, if something.txt has many lines:

    GNU sed evaluate方法。三种有许多行:

    FOO=42 sed 's/"/\\\"/g;s/.*/echo "&"/e' something.txt
    

#9


-1  

The following works: bash -c "echo \"$(cat something.txt)"\"

以下工作:bash -c“echo”$(cat something.txt)“\”