Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bash variable substitution and strings

Let's say I have two variables:

a="AAA"
b="BBB"

I read a string from a file. This string is the following:

str='$a $b'

How to create a new string from the first one that substitutes the variables?

newstr="AAA BBB"
like image 591
mimipc Avatar asked Sep 29 '26 12:09

mimipc


2 Answers

bash variable indirection whithout eval:

Well, as eval is evil, we may try to make this whithout them, by using indirection in variable names.

 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

Another try:

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

Addendum As correctly pointed by @EtanReisner's comment, if your string do contain some * or other glob expendable stings, you may have to use set -f to prevent bad things:

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

Note: I've added " at newstr+=("$cnt"); to prevent glob expansion, but set -f seem required...

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 * *

Nota 2: This is far away from a perfect solution. For sample if string do contain ponctuation, this won't work again... Example:

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

with same variables as previous run will render: ' this string contain a star as *' because ${!var1,} and ${!var3:} don't exist.

... and if $str do contain special chars:

As @godblessfq asked:

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

So this is not robust as every indirected variable must be first, last or space separated from all special chars!

str=$'$var1 world!\n... 2nd line...'
var1=Hello
newstr=()
set -f
IFS=' ' read -d$'\377' -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'}"
like image 147
F. Hauri Avatar answered Oct 02 '26 02:10

F. Hauri


The easiest solution is to use eval:

eval echo "$str"

To assign it to a variable, use command substitution:

replaced=$(eval echo "$str")
like image 45
choroba Avatar answered Oct 02 '26 03:10

choroba