Reference an appended variable? - bash

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}"

Related

How do I pass in an un-evaluated variable to a shell script, and have the script evaluate it after sourcing another script?

I have a script that is sourcing a second script and need to do the following:
I want to pass in a variable name to the first script like this: sh firstScript.sh variable=$variableName
The first script will then source the second script, which contains the value of variableName
I'm then going to print the evaluated variable
I know that I can do something like \$variableName to pass in the variable name, but I can't figure out how to get the first script to then evaluate the variable using the exported variables from the second script. What am I missing here?
Here's what I wound up doing:
I'm passing in an entire string, with the variable embedded in the middle of the string like so:
sh firstScript.sh --message="This is a message \${variableName}"
In the first script, I'm doing these steps:
Extract the entire string in to an array
Pull out the embedded variables
Evaluate the embedded variables against the sourced script
Do a string replace to put the value in the original string
When I was done, it looked like this:
IFS=';' args=(${#//--/;}); unset IFS
source secondScript.sh
case ${arg^^} in
MESSAGE=*)
message="${arg#*=}"
messageVars=$(echo ${message} | grep -o "\${\w*}")
for messageVar in ${messageVars[*]}; do
messageVar=${messageVar#*\{}
messageVar=${messageVar%\}*}
messageVarVal=${!messageVar}
echo "messageVar: ${messageVar}"
echo "messageVarVal: ${messageVarVal}"
message=${message//"\${${messageVar}}"/"${messageVarVal}"}
done
echo "Message: ${message}"
;;
esac
I hope this is what you want:
firstScript.sh
varname="$1"
if [[ -z $varname ]]; then
echo "usage: $0 varname"
exit
fi
source ./secondScript.sh
declare -n ref="$varname"
echo "varname=$varname value=$ref"
secondScript.sh
foo=2
Then execute the script with:
./firstScript.sh foo
Output:
varname=foo value=2
The -n option to the declare creates a reference to another variable.
The bash version should be 4.3 or later to enjoy the functionality.
[Alternative]
You can also write the firstScript.sh as:
firstScript.sh
varname="$1"
if [[ -z $varname ]]; then
echo "usage: $0 varname"
exit
fi
source ./secondScript.sh
echo "varname=$varname value=${!varname}"
which will produce the same result.
The ${!varname} notation is called indirect expansion in which varname is expanded as a name of a variable.
It has been introduced since bash2.
Hope this helps.

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}}`

bash, substitute part of variable by another variable

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

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

How can you get a variable's value given its name in korn shell?

Is there a way in ksh to get a variable's value when you have been given the name of the variable?
For example:
#!/usr/bin/ksh
var_name=$1 #pretend here that the user passed the string "PATH"
echo ${$var_name} #echo value of $PATH -- what do I do here?
eval `echo '$'$var_name`
echo concatenates a '$' to the variable name inside $var_name, eval evaluates it to show the value.
EDIT:
The above isn't quite right. The correct answer is with no backticks.
eval echo '$'$var_name
printenv is not a ksh builtin and may not always be present. For older ksh versions, prior to ksh93, the eval 'expression' method works best.
A powerful method in ksh93 is to use indirection variables
with 'nameref' or 'typeset -n'.
Define and verify a nameref variable that refers to $PATH:
$ nameref indirect=PATH
$ print $indirect
/usr/bin:/usr/sbin
See how the nameref variable changes when we change PATH:
$ PATH=/usr/bin:/usr/sbin:/usr/local/bin
$ print $indirect
/usr/bin:/usr/sbin:/usr/local/bin
Show ksh version and the alias for nameref:
$ type nameref
nameref is an alias for 'typeset -n'
$ echo ${.sh.version}
Version JM 93t+ 2010-02-02
var_name=$1 #pretend here that the user passed the string "PATH"
printenv $var_name
For one step above your answer (I spent a lot of time trying to find both these answers). The below will allow you to export a dynamic variable and then recall it dynamically:
echo -n "Please provide short name for path:"
read PATH_SHORTCUT
echo -n "Please provide path:"
read PATH
eval export \${PATH_SHORTCUT}_PATH="${PATH}"
eval echo Path shortcut: ${PATH_SHORTCUT} set to \$"${PATH_SHORTCUT}_PATH".

Resources