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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10683349/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 06:24:31  来源:igfitidea点击:

Forcing bash to expand variables in a string loaded from a file

linuxbashshellunix

提问by Michael Neale

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

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

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

我有一个名为“something.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。我无法评估或获取文件 - 因为它会尝试执行它(它不是可执行的 - 我只想要插入变量的字符串)。

Any ideas?

有任何想法吗?

采纳答案by LenW

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

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

envsubst < something.txt

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

如果它在您的发行版中尚不可用,则在

GNU package gettext.

GNU 包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/7088for 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 [[  = -h ]]  ;then echo -e >&2  "$Usage" ; exit 1 ;fi
if [[  = -sl ]] ;then  sl='\'  ; shift ;fi

sed 's/\$/${EnVsUbDolR}/g' |  EnVsUbDolR=$sl$  envsubst  "$@"

回答by Todd A. Jacobs

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

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

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

See help evalor the Bash manual for more information.

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

回答by pizza

you can try

你可以试试

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

回答by Michael Neale

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:

将 something.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 !

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

回答by wjl

Many of the answers using evaland echokind 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.

许多答案使用evalecho工作类型,但在各种事情上中断,例如多行,尝试转义 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=""
    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.cfgwhich is really a shell script that just sets variables, and a template.txtwhich is a template that uses those variables:

例如,您可以像这样使用 a parameters.cfg,它实际上是一个只设置变量的 shell 脚本,而 atemplate.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.

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

回答by SS781

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 周围的转义引号是这里的关键)

回答by aprodan

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

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

  • 允许替换定义的变量

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

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

    ${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

回答by agc

  1. If something.txthas only oneline, a bashmethod, (a shorter version of Michael Neale's"icky" answer), using process& command substitution:

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

    Output:

    hello 42 world
    

    Note that exportisn't needed.

  2. If something.txthas one or more lines, a GNUsedevaluatemethod:

    FOO=42 sed 's/"/\\"/g;s/.*/echo "&"/e' something.txt
    
  1. 如果something.txt只有一个线,bash方法,(短版的迈克尔·尼尔的“恶心”的答案),使用过程命令替换

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

    输出:

    hello 42 world
    

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

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

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

回答by Eddy

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

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

回答by ClearCrescendo

$ 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.