Indirect expansion returns variable name instead of value - linux

I am trying to set up some variables using indirect expansion. According to the documentation I've read, the set up should be simple:
var1=qa
qa_num=12345
varname="${var1}_ci"
echo ${!varname}
I should be getting "12345". Instead, the output is "varname". If I remove the exclamation point, I end up with "qa_ci", not "12345"
This should be a relatively simple solution, so I'm not sure what I'm missing, if anything.

Your code defines qa_num, but the varname assignment references qa_ci. As a result, your echo was expanding nonexistent qa_ci, giving empty results. Changing the varname assignment fixes the problem on my system.
Example: foo.sh:
#!/bin/bash
var1=qa
qa_num=12345
varname="${var1}_num" # <=== not _ci
echo "${!varname}" # I also added "" here as a general good practice
Output:
$ bash foo.sh
12345

Related

Bash don't replace $ [duplicate]

This question already has answers here:
Difference between single and double quotes in Bash
(7 answers)
Closed 1 year ago.
I want to write a bash script which creates other bash scripts. But when I do
echo "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:~/SOME/PATH" >> NEWFILE.sh
it already replaces $LD_LIBRARY_PATH in the first script.
So in NEWFILE.sh I only get:
export LD_LIBRARY_PATH=:~/SOME/PATH
But i want that in the NEWFILE.sh there's still:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:~/SOME/PATH
So it gets replaced, when running NEWFILE.sh. I hope this makes sense. Thank you for your help
If you don't want the variable interpolated, use single quotes:
echo 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:~/SOME/PATH' >> NEWFILE.sh
But even this small snippet has issues. When NEWFILE.sh runs with an empty LD_LIBRARY_PATH, it will create an invalid value that has a leading :. Also, it is bad practice to use ~ in variables. Instead, you should do:
echo 'export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}${LD_LIBRARY_PATH:+:}$HOME/SOME/PATH' >> NEWFILE.sh
Also, this snippet makes NEWFILE.sh not idempotent, and you may wind up with multiple instances of $HOME/SOME/PATH in the final value. This is easy enough to avoid, but takes a bit more logic. Something like:
cat << \EOF >> NEWFILE.sh
pathmunge () { case ":${LD_LIBRARY_PATH}:" in *:"$1":*) ;;
*) LD_LIBRARY_PATH="$LD_LIBRARY_PATH${LD_LIBRARY_PATH:+:}$1";; esac; }
pathmunge $HOME/SOME/PATH
export LD_LIBRARY_PATH
EOF
This uses a common technique of quoting the HEREDOC (the backslash before the EOF is essential; see how it changes when you remove the leading backslash) to prevent interpolation, and allows multi-line output and quotes to be used in the content that is going to be written to NEWFILE.sh.
One small point is that this will wind up putting the expansion of $HOME in LD_LIBRARY_PATH, which is probably what you want. If you really do want $HOME in the path (so that it is expanded when LD_LIBRARY_PATH is used, rather than when it is defined), you could do pathmunge '$HOME/path', but you may wind up with duplicate instances in the final value, since pathmunge will not recognize the unexpanded value as matching the expanded value. Avoiding that duplication is an exercise left for the reader.
Note that, depending on the remaining content of NEWFILE.sh, you may want to ensure that pathmunge is only defined once, and that this definition is not overriding some other definition. pathmunge is a common name for a function used for modifying PATH so you may want to consider a different name, or adding logic to allow it to take the name of the variable to be overridden.
You should escape the $ as follow:
echo "export LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:~/SOME/PATH" >> NEWFILE.sh

Unix, bad substitution error?

I have the following variables :
TYP=a1
STAT_a1=statistical
FINAL_VARIABLE=${STAT_${TYP}}
But I get an error:
-bash: ${STAT_${TYP}} : bad substitution
What I want is, that the value of FINAL_VARIABLE be 'statistical'
Please help..
You can't nest variable expansions like that. But you can use indirect variable expansion with ${!varname}:
TYP=a1
STAT_a1=statistical
STAT_var=STAT_${TYP} # This sets STAT_var to "STAT_a1"
FINAL_VARIABLE=${!STAT_var} # This sets FINAL_VARIABLE to "statistical"
BTW, I recommend avoiding all-caps variable names like TYP and FINAL_VARIABLE -- there are a number of all-caps names with special meanings to the shell and/or other programs, and if you accidentally use one of those weird things can happen.
Use eval, wrapping all the stuff you want to defer evaluating in single quotes:
eval 'FINAL_VARIABLE=${STAT_'${TYP}'}'
$ TYPE=al
$ STAT_a1=statistical
$ eval 'FINAL_VARIABLE=${STAT_'${TYP}'}'
$ echo $FINAL_VARIABLE
statistical
You can also do like this
root#myagent: tmp$ F=$(echo "${STAT_a1}"_"${TYP}")
root#myagent: tmp$ echo $F
statistical_a1

can't read bash script

I ran into this syntax
export ts=${2:-`date "+%s"`}
I know about export and ${2}. I even understand +%s
what does it mean to add :- behind it?
googling these symbols is useless. where do you look up things like this?
The dash causes the expansion to be the value of the variable if it is defined, or the expansion of what follows if the variable is not defined.
Example:
AA=aa
echo ${AA:-11}
echo ${BB:-22}
Will produce the output:
aa
22
Because AA is defined and BB is not.
It means "If the second command line argument has not been passed to the program, use the following value":
`date "+%s"`
It is called "parameter substitution" and is documented here.

create variables inside unix script

I am trying to create a variable in a script, based on another variable.
I just don't know what needs to be adjusted in my code, if it is possible.
I am simplifying my code for your understanding, so this is not the original code.
The code goes like that:
#!/bin/csh -f
set list_names=(Albert Bela Corine David)
set Albert_house_number=1
set Bela_house_number=2
set Corine_house_number=3
set David_house_number=4
foreach name ($list_names)
#following line does not work....
set house_number=$($name\_house_number)
echo $house_number
end
the desired output should be:
1
2
3
4
Thanks for your help.
Unfortunately, the bashism ${!varname} is not available to us in csh, so we'll have to go the old-fashioned route using backticks and eval. csh's quoting rules are different from those of POSIX-conforming shells, so all of this is csh specific. Here we go:
set house_number = `eval echo \" \$${name}_house_number\"`
echo "$house_number"
${name} is expanded into the backticked command, so this becomes equivalent to, say,
set house_number = `eval echo \" \$Albert_house_number\"`
which then evaluates
echo " $Albert_house_number"
and because of the backticks, the output of that is then assigned to house_number.
The space before \$$ is necessary in case the value of the expanded variable has special meaning to echo (such as -n). We could not simply use echo "-n" (it wouldn't print anything), but echo " -n" is fine.1
The extra space is stripped by csh when the backtick expression is expanded. This leads us to the remaining caveat: Spaces in variable values are going to be stripped; csh's backticks do that. This means that if Albert_house_number were defined as
set Albert_house_number = "3 4"
house_number would end up with the value 3 4 (with only one space). I don't know a way to prevent this.
1 Note that in this case, the echo "$house_number" line would have to be amended as well, or it would run echo "-n" and not print anything even though house_number has the correct value.

set default values for bash variables only if they were not previously declared

Here's my current process:
var[product]=messaging_app
var[component]=sms
var[version]=1.0.7
var[yum_location]=$product/$component/$deliverable_name
var[deliverable_name]=$product-$component-$version
# iterate on associative array indices
for default_var in "${!var[#]}" ; do
# skip variables that have been previously declared
if [[ -z ${!default_var} ]] ; then
# export each index as a variable, setting value to the value for that index in the array
export "$default_var=${var[$default_var]}"
fi
done
The core functionality I'm looking for is to set a list of default variables that will not overwrite previously declared variables.
The above code does that, but it also created the issue of these variables can now not depend on one another. This is because the ordering of the associative array's indices output from "${!var[#]}" is not always the same as the order they were declared in.
Does a simpler solution exist like:
declare --nooverwrite this=that
I haven't been able to find anything akin to that.
Also, I'm aware that this can be done with an if statement. However using a bunch of if statements would kill the readability on a script with near 100 default variables.
From 3.5.3 Shell Parameter Expansion:
${parameter:=word}
If parameter is unset or null, the expansion of word is assigned to parameter. The value of parameter is then substituted. Positional parameters and special parameters may not be assigned to in this way.
So
: ${this:=that}
: needed because otherwise the shell would see ${this:=that} as a request to run, as a command, whatever that expanded to.
$ echo "$this"
$ : ${this:=that}
$ echo "$this"
that
$ this=foo
$ echo "$this"
foo
$ : ${this:=that}
$ echo "$this"
foo
You can also to this the first place you use the variable (instead of on its own) if that suits things better (but make sure that's clear because it is easy to mess that up in later edits).
$ echo "$this"
$ echo "${this:=that}"
that
$ echo "$this"
that
Doing this dynamically, however, is less easy and may require eval.

Resources