Bash 变量替换和字符串

Bash variable substitution and strings

假设我有两个变量:

a="AAA"
b="BBB"

我从文件中读取了一个字符串。该字符串如下:

str='$a $b'

如何从替换变量的第一个字符串创建新字符串?

newstr="AAA BBB"

最简单的解决方案是使用 eval:

eval echo "$str"

要将其分配给变量,请使用命令替换:

replaced=$(eval echo "$str")

变量 间接 eval:

好吧,因为 eval 邪恶的 ,我们可以尝试在没有它们的情况下通过在变量中使用 indirection名字。

 a="AAA"
 b="BBB"
 str='$a $b'

 newstr=()
 for cnt in $str ;do
     [ "${cnt:0:1}" == '$' ] && cnt=${cnt:1} && cnt=${!cnt}
     newstr+=($cnt)
   done
 newstr="${newstr[*]}"

 echo $newstr
 AAA BBB

再试一次:

var1="Hello"
var2="2015"

str='$var1 world! Happy new year $var2'

newstr=()
for cnt in $str ;do
    [ "${cnt:0:1}" == '$' ] && cnt=${cnt:1} && cnt=${!cnt}
    newstr+=($cnt)
  done
newstr="${newstr[*]}"

echo $newstr 
Hello world! Happy new year 2015

附录 正如@EtanReisner 的评论所正确指出的那样,如果您的字符串确实包含一些 * 或其他 glob 消耗性字符串,你可能不得不使用 set -f 来防止坏事:

cd /bin
var1="Hello"
var2="star"
var3="*"
str='$var1 this string contain a $var2 as $var3 *'

newstr=()
for cnt in $str ;do
     [ "${cnt:0:1}" == '$' ] && cnt=${cnt:1} && cnt=${!cnt};
     newstr+=("$cnt");
   done;
newstr="${newstr[*]}"

echo "$newstr"
Hello this string contain a star as * bash bunzip2 busybox....zmore znew

echo ${#newstr}
1239

注意: 我在 newstr+=("$cnt"); 处添加了 " 以防止 glob 扩展,但 set -f 似乎是必需的...

newstr=()
set -f
for cnt in $str ;do
    [ "${cnt:0:1}" == '$' ] && cnt=${cnt:1} && cnt=${!cnt}
    newstr+=("$cnt")
  done
set +f
newstr="${newstr[*]}"

echo "$newstr"
Hello this string contain a star as * *

注2:这离完美的解决方案还有很长的路要走。例如,如果字符串确实包含标点符号,这将不再有效...示例:

str='$var1, this string contain a $var2 as $var3: *'

使用与之前相同的变量 运行 将呈现: ' this string contain a star as *' 因为 ${!var1,}${!var3:} 不存在。

... 如果 $str 确实包含特殊字符:

正如 @godblessfq 所问:

If str contains a line break, how do I do the substitution and preserve the newline in the output?

所以这不是稳健,因为每个间接变量必须是第一个、最后一个或 space 与所有特殊字符分开

str=$'$var1 world!\n... 2nd line...'
var1=Hello
newstr=()
set -f
IFS=' ' read -d$'7' -ra array <<<"$str"
for cnt in "${array[@]}";do
    [ "${cnt:0:1}" == '$' ] && cnt=${cnt:1} && cnt=${!cnt}
    newstr+=("$cnt")
  done
set +f
newstr="${newstr[*]}"

echo "$newstr"
Hello world!
... 2nd line...

As <<< inline string add a trailing newline, last echo command could be written:

echo "${newstr%$'\n'}"

免责声明:我 perl 一小时前才发现。但这似乎可以很好地工作,无论您输入什么特殊字符:

newstr=$(a2="$a" b2="$b" perl -pe 's/$a\b/$ENV{a2}/g; s/$b\b/$ENV{b2}/g' <(echo -e "$str"))

测试:

a='A*A\nA'
b='B*B\nB'
str='$a $aa * \n $b $bb'

newstr=$(a2="$a" b2="$b" perl -pe 's/$a\b/$ENV{a2}/g; s/$b\b/$ENV{b2}/g' <(echo -e "$str"))

echo -e "$newstr"

输出:

A*A
A $aa * 
 B*B
B $bb