bash, substitute part of variable by another variable - bash

#!/bin/bash
SUNDAY_MENU=BREAD
MONDAY_MENU=APPLES
TODAY=MONDAY
ECHO "I want ${${TODAY}_MENU}" # does not work, bad substitution
ECHO "I want ${`echo $TODAY`_MENU}" # does not work, bad substitution
Any Ideas ?

Use variable indirection like this:
varname=${TODAY}_MENU
echo ${!varname}
If you are using Bash 4 or later, however, you are probably better off using an associative array:
menu=([sunday]=bread [monday]=apples)
echo ${menu[$TODAY]}

I use eval function
#!/bin/bash
SUNDAY_MENU=BREAD
MONDAY_MENU=APPLES
TODAY=MONDAY
eval TODAY_MENU=\$\{${TODAY}_MENU\}
echo "I want ${TODAY_MENU}"

Related

Bad Substitution - Variable name inside other variable name - in Bash

I have a problem in one of my scripts, here it is simplified.
I want to name a variable using another variable in it. My script is:
#! /bin/bash
j=1
SAMPLE${j}_CHIP=5
echo ${SAMPLE${j}_CHIP}
This script echoes:
line 3: SAMPLE1_CHIP=5: command not found
line 4: ${SAMPLE${j}_CHIP}: bad substitution
I'm trying to do that in order to name several samples in a while loop changing the "j" parameter.
Anyone knows how to name a variable like that?
It's possible with eval, but don't use dynamic variable names. Arrays are much, much better.
$ j=1
$ SAMPLES[j]=5
$ echo ${SAMPLES[j]}
5
You can initialize an entire array at once like so:
$ SAMPLES=(5 10 15 20)
And you can append with:
$ SAMPLES+=(25 30)
Indices start at 0.
To read the value of the variable, you may use indirection: ${!var}:
#! /bin/bash
j=1
val=get_5
var=SAMPLE${j}_CHIP
declare "$var"="$val"
echo "${!var}"
The problem is to make the variable get the value.
I used declare above, and the known options are:
declare "$var"="$val"
printf -v "$var" '%s' "$val"
eval $var'=$val'
export "$var=$val"
The most risky option is to use eval. If the contents of var or val may be set by an external user, you have set a way to get code injection. It may seem safe today, but after someone edit the code for some reason, it may get changed to give an attacker a chance to "get in".
Probably the best solution is to avoid all the above.
Associative Array
One alternative is to use Associative Arrays:
#! /bin/bash
j=1
val=get_5
var=SAMPLE${j}_CHIP
declare -A array
array[$var]=$val
echo "${array[$var]}"
Quite less risky and you get a similar named index.
Plain array
But it is clear that the safest solution is to use the simplest of solutions:
#! /bin/bash
j=1
val=get_5
array[j]=$val
echo "${array[j]}"
All done, little risk.
If you really want to use variable variables:
#! /bin/bash
j=1
var="SAMPLE${j}_CHIP"
export ${var}=5
echo "${!var}" # prints 5
However, there are other approaches to solving the parent issue, which are likely less confusing than this approach.
j=1
eval "SAMPLE${j}_CHIP=5"
echo "${SAMPLE1_CHIP}"
Or
j=1
var="SAMPLE${j}_CHIP"
eval "$var=5"
echo "${!var}"
As others said, it's normally not possible. Here is a workaround if you wish. Note that you have to use eval when declaring a nested variable, and ⭗ instead of $ when accessing it (I use ⭗ as a function name, because why not).
#!/bin/bash
function ⭗ {
if [[ ! "$*" = *\{*\}* ]]
then echo $*
else ⭗ $(eval echo $(echo $* | sed -r 's%\{([^\{\}]*)\}%$(echo ${\1})%'))
fi
}
j=1
eval SAMPLE${j}_CHIP=5
echo `⭗ {SAMPLE{j}_CHIP}`
c=CHIP
echo `⭗ {SAMPLE{j}_{c}}`

Reference an appended variable?

How can I do this in a bash script?
#!/bin/sh
func() {
export ${NAME}_SUFFIX=`result_of_some_command`
}
NAME=my_name
func
# This variable will become my_name_SUFFIX
# but how can I reference it using $NAME?
echo ${${NAME}_SUFFIX} # Doesn't work...
I'd use declare if it would work through function calls, but it seems it doesn't. Also, my version of declare doesn't support -x.
You can use indirect parameter expansion:
varname=${NAME}_SUFFIX
echo "${!varname}"
bash 4.3 also introduced namerefs, which are a little simpler to use.
$ NAME=FOO
$ FOO_SUFFIX=3
$ declare -n varname=${NAME}_SUFFIX
$ echo $varname
3
You need variable indirection, which is introduced with a ! :
var_name=test
test=value
echo ${var_name} # will echo test
echo ${!var_name} # will echo value
I know what you're trying to do...warning..."eval" is a terrible way to do it and variable indirection is better. But, here is what you asked for.
One simple change to your script:
Change "export" to "eval" and it'll work. You were most of the way there!
Oh, and change your "echo" statement as shown below.
Example:
#!/bin/sh
func() {
eval ${NAME}_SUFFIX=`result_of_some_command`
}
NAME=my_name
func
# This variable will become my_name_SUFFIX
# but how can I reference it using $NAME?
eval echo "\${${NAME}_SUFFIX}"

using variable to make variable name in shell script

I want to echo $ANMIAL0 and then $ANIMAL1 using a script below.
But I get line 7: ${ANIMAL$i}: bad substitution error message. What's wrong?
#!/bin/sh
ANIMAL0="tiger"
ANIMAL1="lion"
i=0
while test $i -lt 2; do
echo "Hey $i !"
echo ${ANIMAL$i}
i=`expr $i + 1`
done
You're probably better off using an array instead of ANIMAL0 and ANIMAL1. Something like this maybe?
#!/bin/bash
animals=("Tiger" "Lion")
for animal in ${animals[*]}
do
printf "Hey, ${animal} \n"
done
Using eval will get you into trouble down the road and is not best practice for what you're trying to do.
The problem is that the shell normally performs a single substitution pass. You can force a second pass with eval, but this obviously comes with the usual security caveats (don't evaluate unchecked user input, etc).
eval echo \$ANIMAL$i
Bash has various constructs to help avoid eval.
You can use eval
eval echo \$ANIMAL$i

How to concatenate a string with a variable to the new variable in Bash?

I have a shell script like this,
#!/bin/bash
foxy1="foxyserver"
H="1"
and the output should be foxyserver.
I tried this,
echo $foxy$H
and this gives me
1
and then I used
str="foxy$H"
echo $str
the output is
foxy1
How could I do that?
Use indirect variables, as described in BashFAQ #6:
$ foxy1="foxyserver"
$ H="1"
$ varname="foxy$H"
$ echo "${!varname}"
foxyserver
Using eval, you can do indirection:
eval echo \$$str
Output
foxyserver
Warning: This is not really good practice. For example, if you have str=(rm -rf ~/*) then the eval expression would be $(rm -rf ~/*). So be warned and use indirection as suggested by Charles Duffy.

Expanding variables Bash Scripting

I have two variables in bash that complete the name of another one, and I want to expand it but don't know how to do it
I have:
echo $sala
a
echo $i
10
and I want to expand ${a10} in this form ${$sala$i} but apparently the {} scape the $ signs.
There are a few ways, with different advantages and disadvantages. The safest way is to save the complete parameter name in a single parameter, and then use indirection to expand it:
tmp="$sala$i" # sets $tmp to 'a10'
echo "${!tmp}" # prints the parameter named by $tmp, namely $a10
A slightly simpler way is a command like this:
eval echo \${$sala$i}
which will run eval with the arguments echo and ${a10}, and therefore run echo ${a10}. This way is less safe in general — its behavior depends a bit more chaotically on the values of the parameters — but it doesn't require a temporary variable.
Use the eval.
eval "echo \${$sala$i}"
Put the value in another variable.
result=$(eval "echo \${$sala$i}")
The usual answer is eval:
sala=a
i=10
a10=37
eval echo "\$$sala$i"
This echoes 37. You can use "\${$sala$i}" if you prefer.
Beware of eval, especially if you need to preserve spaces in argument lists. It is vastly powerful, and vastly confusing. It will work with old shells as well as Bash, which may or may not be a merit in your eyes.
You can do it via indirection:
$ a10=blah
$ sala=a
$ i=10
$ ref="${sala}${i}"
$ echo $ref
a10
$ echo ${!ref}
blah
However, if you have indexes like that... an array might be more appropriate:
$ declare -a a
$ i=10
$ a[$i]="test"
$ echo ${a[$i]}
test

Resources