How to use the value of a string in bash as a variable name - linux

Hopefully I can make it clear. I would like to create a filename out of different strings in bash. For example hmd.sh so h, m, d are different values (number 0..9 or letter aA..zZ). So for example I want to convert
h=1 m=11 and d=12 to 1aA.sh. h=> 1, m=>a and d=>A
To declare variables like
a01=1; a02=2 .. a09=9, a10=0; a11=a; a12=b and so on. h(1)=a01=1 m(11)=a11=a
and
d(12)=a12=A.
To test it I wrote this:
#!/bin/bash
dd01="1"
aa="01"
bb="dd$aa"
echo $bb
But of course $bb is dd01 and not its value. How can I make $bb its value of 1?

Associative arrays make this kind of thing much more readable.
However your answer is "variable indirection"
$ echo $bb
dd01
$ echo ${!bb}
1
Do not listen to any advice suggesting eval -- you open yourself up to all kinds of code injection.

The only way to expand a variable inside another is in an array when the variable is enclosed in the key's brackets like [$var].
You could store your values in an associative array, and reference them like so:
declare -A arr
arr[dd01]="1"
arr[aa]="01"
arr[bb]="dd${arr[aa]}"
echo ${arr[${arr[bb]}]}
Using arrays like this may be more convoluted for this example than referencing the variable name using ${!bb} syntax, but if you need to do this while keeping different sets of variables that may need to reference each other, creating an associative array may make more organizational sense.

I rewrote your code example as follows which gives you the value of 1 which is what you are trying to achieve.
#!/bin/bash
dd01="1"
aa="01"
bb="dd$aa"
echo $[$bb]

This did the trick:
#!/bin/bash
dd=1234567890aAbBcC
aa="11"
echo ${dd:(aa-1):1}
Appearantly 1 is the 0 position and aa can also be like 01 and still work!
Thank for all the advices.
I found my answer here:
http://tldp.org/LDP/abs/html/string-manipulation.html

This should do the job:
#!/bin/bash
dd01="1"
aa="01"
bb="dd$aa"
eval echo \$$bb

You can use the '$' operator to access the value of a variable. For example...
d = 'Hi'
e = ' there '
f = 'friend.'
foo = '$d$e$f'
This would cause the value of foo to be 'Hi there friend.'
Hope this helps.

Use eval:
#!/bin/bash
dd01="1"
aa="01"
eval bb=\$dd$aa
echo $bb
This script outputs the expected 1.

Related

Copy a bash associative array using eval statement

Before we get into the question, I know there are answers SIMILAR to this on stack overflow already. However this one is unique in it's use of the eval statement with associative arrays. ( Believe me I've read them all ).
Okay now into the question
I have X number of arrays defined via an eval function similar to this:
for (( i=1;i<=X;i++ ))
do
eval "declare -gA old$i"
eval "old$i[key]=value"
done
This code is in function : makeArrays
Now I have a second function that must loop through these different arrays
old1
old2
.
.
.
oldX
I'll call this function : useArrays
Now, I have a for loop for this useArrays function.
for (( i=0;i<$#;i++ ))
do
// ACCESS OLD1[KEY]
done
My question is, how do I access this array FOR COMPARISONS.
I.E.
if[ old1 -eq 0 ]
then
...
fi
Is there a way I could COPY these associate arrays into a variable I can use for comparisons using eval as little as possible?
Modern versions of bash support namerefs (originally a ksh feature), so you can point a constant name at any variable you choose; this makes eval unnecessary for the purposes to which you're presently placing it.
key="the key you want to test"
for (( i=0;i<$#;i++ )); do
declare -n "oldArray=old$i" # map the name oldArray to old0/old1/old2/...
printf 'The value of %q in old%q is: %q\n' "$key" "$i" "${oldArray[$key]}"
unset -n "oldArray" # remove that mapping
done
You could of course refer to "${!oldArray[#]}" to iterate over its keys; also map a newArray namevar to compare with; etc.

Get variable name while iterating over array in bash

What I have is an array with some variables. I can iterate to get the values of those vars but what I need is actually their names (values will be used elsewhere).
Going with var[i] won't work cause I will have different names. I guess I could workaround this by creating another array with the names - something similar to this:
Getting variable values from variable names listed in array in Bash
But I'm wondering if there is a better way to do this.
var1=$'1'
var2=$'2'
var3=$'3'
Array=( $var1 $var2 $var3)
for ((i=0; i<${#Array[#]}; i++))
do
echo ${Array[i]}
done
Is:
>1
>2
>3
Should be:
>var1
>var2
>var3
It sounds like you want an associative array.
# to set values over time
declare -A Array=( ) || { echo "ERROR: Need bash 4.0 or newer" >&2; exit 1; }
Array[var1]=1
Array[var2]=2
Array[var3]=3
This can also be assigned at once:
# or as just one assignment
declare -A Array=( [var1]=1 [var2]=2 [var3]=3 )
Either way, one can iterate over the keys with "${!Array[#]}", and retrieve the value for a key with ${Array[key]}:
for var in "${!Array[#]}"; do
val="${Array[$var]}"
echo "$var -> $val"
done
...will, after either of the assignments up top, properly emit:
var1 -> 1
var2 -> 2
var3 -> 3
What about this solution?
#!/bin/bash
var1=$'1'
var2=$'2'
var3=$'3'
Array=( var1 var2 var3 )
for var in "${Array[#]}"; do
echo "$var = ${!var}"
done
The idea just consists in putting your variable names in the array, then relying on the indirection feature of Bash.
But as pointed out by #CharlesDuffy, the use of associative arrays sounds better adapted to the OP's use case.
Also, this related article may be worth reading: How can I use variable variables… or associative arrays?

How do you compare the value of an array to a variable in bash script?

I'm practicing bash and honestly, it is pretty fun. However, I'm trying to write a program that compares an array's value to a variable and if they are the same then it should print the array's value with an asterisk to the left of it.
#!/bin/bash
color[0]=red
color[1]=blue
color[2]=black
color[3]=brown
color[4]=yellow
favorite="black"
for i in {0..4};do echo ${color[$i]};
if {"$favorite"=$color[i]}; then
echo"* $color[i]"
done
output should be *black
There's few incorrect statements in your code that prevent it from doing what you ask it to. The comparison in bash is done withing square brackets, leaving space around them. You correctly use the = for string comparison, but should enclose in " the string variable. Also, while you correctly address the element array in the echo statement, you don't do so inside the comparison, where it should read ${color[$i]} as well. Same error in the asterisk print. So, here a reworked code with the fixes, but read more below.
#!/bin/bash
color[0]=red
color[1]=blue
color[2]=black
color[3]=brown
color[4]=yellow
favorite=black
for i in {0..4};do
echo ${color[$i]};
if [ "$favorite" = "${color[$i]}" ]; then
echo "* ${color[$i]}"
fi
done
While that code works now, few things that probably I like and would suggest (open to more expert input of course by the SO community): always enclose strings in ", as it makes evident it is a string variable; when looping an array, no need to use index variables; enclose variables always within ${}.
So my version of the same code would be:
#!/bin/bash
color=("red" "blue" "black" "brown" "yellow")
favorite="black"
for item in ${color[#]}; do
echo ${item}
if [ "${item}" = "${favorite}" ]; then
echo "* $item"
fi
done
And a pointer to the great Advanced Bash-Scripting Guide here: http://tldp.org/LDP/abs/html/

Assigning one variable to another in Bash?

I have a doubt. When i declare a value and assign to some variable, I don't know how to reassign the same value to another variable. See the code snippet below.
#/bin/sh
#declare ARG1 to a
a=ARG1
#declaring $a to ARG2
ARG2=$`$a`
echo "ARG 2 = $ARG2"
It should display my output as
ARG 2 = ARG1
...but instead the actual output is:
line 5: ARG1: command not found
ARG 2 = $
To assign the value associated with the variable dest to the variable source, you need simply run dest=$source.
For example, to assign the value associated with the variable arg2 to the variable a:
a=ARG1
arg2=$a
echo "ARG 2 = $arg2"
The use of lower-case variable names for local shell variables is by convention, not necessity -- but this has the advantage of avoiding conflicts with environment variables and builtins, both of which use all-uppercase names by convention.
You may also want to alias rather than copy the variable. For example, if you need mutation. Or if you want to run a function multiple times on different variables. Here's how it works
Example:
C=cat
declare -n VAR=C
VAR+=" says Hi"
echo "$C" # prints "cat says Hi"
Example with arrays/dictionaries:
A=(a a a)
declare -n VAR=A # "-n" stands for "name", e.g. a new name for the same variable
VAR+=(b)
echo "${A[#]}" # prints "a a a b"
That is, VAR becomes effectively the same as the original variable. Instead of copying, you're adding an alias. Here's an example with functions:
function myFunc() {
local -n VAR="$1"
VAR="Hello from $2"
echo "I've set variable '$1' to value '$VAR'"
}
myFunc Inbox Bob # I've set variable 'Inbox' to value 'Hello from Bob'
myFunc Luke Leia # I've set variable 'Luke' to value 'Hello from Leia'
echo "$Luke" # Hello from Leia
Whether you should use these approaches is a question. Generally, immutable code is easier to read and to reason about (in almost any programming language). However, sometimes you really need to get stuff done in a certain way. Hope this answer helps you then.

Bash scripting - Iterating through "variable" variable names for a list of associative arrays

I've got a variable list of associative arrays that I want to iterate through and retrieve their key/value pairs.
I iterate through a single associative array by listing all its keys and getting the values, ie.
for key in "${!queue1[#]}" do
echo "key : $key"
echo "value : ${queue1[$key]}"
done
The tricky part is that the names of the associative arrays are variable variables, e.g. given count = 5, the associative arrays would be named queue1, queue2, queue3, queue4, queue5.
I'm trying to replace the sequence above based on a count, but so far every combination of parentheses and eval has not yielded much more then bad substitution errors. e.g below:
for count in {1,2,3,4,5} do
for key in "${!queue${count}[#]}" do
echo "key : $key"
echo "value : ${queue${count}[$key]}"
done
done
Help would be very much appreciated!
The difficulty here stems from the fact that the syntax for indirect expansion (${!nameref}) clashes with the syntax for extracting keys from an associative arrays (${!array[#]}). We can have only one or the other, not both.
Wary as I am about using eval, I cannot see a way around using it to extract the keys of an indirectly referenced associative array:
keyref="queue${count}[#]"
for key in $(eval echo '${!'$keyref'}'); do ... ; done
You can however avoid eval and use indirect expansion when extracting a value from an array given the key. Do note that the [key] suffix must be part of the expansion:
valref="queue${count}[$key]"
echo ${!valref}
To put this in context:
for count in {1..5} ; do
keyref="queue${count}[#]"
for key in $(eval echo '${!'$keyref'}'); do
valref="queue${count}[$key]"
echo "key = $key"
echo "value = ${!valref}"
done
done
I was able to make it work with the following script:
for count in {1..5} ; do
for key in $(eval echo '${!q'$count'[#]}') ; do
eval echo '${q'$count"[$key]}"
done
done
Note it breaks if any key contained a space. If you want to deal with complex data structures, use a more powerful language like Perl.
I think this might work (but untested). The key is to treat the indexing
as the full name of a variable. (That is, the array queue5 can be
treated as a sequence of variables named queue5[this], queue5[that], etc.)
for count in {1,2,3,4,5} do
assoc="queue$count[#]"
for key in "${!assoc}" do
echo "key : $key"
val="queue$count[$key]"
echo "value : ${!val}"
done
done

Resources