LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   Need to generate an array based on user input (https://www.linuxquestions.org/questions/programming-9/need-to-generate-an-array-based-on-user-input-4175448427/)

soupmagnet 02-03-2013 01:05 AM

Need to generate an array based on user input
 
I need to generate a range of variables based on user input. The user will first choose the number of variables in the range, then assign a value to each of the variables. I'm trying to do this in a loop but I'm running into problems.

This is what I have to create the range of variables:

Code:

read -p "Enter a number for range of variables: " foo

myfunction(){
(( bar = $foo + 1 ))
let count=1
until [ $count -eq $bar ]; do
echo var"$count"
(( count = $count + 1 ))
done | tr "\n" " " | sed 's/\ $//'
}

If the user enters 5, the output will be
Code:

var1 var2 var3 var4 var5
If I try to assign a value to each of the variables things get kind of weird.

Code:

for i in `myfunction`; do
  read -p "Enter a value" value
  let $i=$value
  echo $i
  echo $var1
done

...which outputs
Code:

var1
0

...if the value is a string,

but
Code:

1234
<empty line>

if the value is a number.

Why isn't the value being assigned to the variable?

millgates 02-03-2013 08:45 AM

let treats the expression as an arithmetic expression, so if you pass a string there, don't expect to get anything reasonable.
What are you trying to do in the first place?
Why don't you just use arrays?

soupmagnet 02-03-2013 10:29 AM

I'm working on a repartitioning script for Android

I know I can do something like this...
Code:

name1=boot; name2=system; name3=data; name4=cache; name5=media
size1=11; size2=500; size3=900; size4=256; size5=5000

NAME=(0 $name1 $name2 $name3 $name4 $name5)
SIZE=(0 $size1 $size2 $size3 $size4 $size5)

for i in {1..5}; do
  echo ${NAME[$i]}
  echo ${SIZE[$i]}
done

I have the majority of the script finished and working properly, but I want to add an option to let users choose not only the names and sizes of the partitions, but also the overall number of partitions.

I'm trying to figure out a way to assign a value to a string of text, as it's created in the script, based on the user's input.


I figured that was the case with the 'let' command, but trying to do something like...
Code:

$i=$value
will give an error
I've also tried...
Code:

`echo $i`=`echo $value`
but I receive the same error "command not found"

I'm trying to do it this way because the values of 'size1 size2 size3 etc...' can be changed at any point and I want to be able to use one script and just plug in the new values of those variables.

Does that make any sense at all?


I guess when it's all said and done, I'd like to be able to change the values of the variables in both $NAME and $SIZE, on the fly. Is that possible?

millgates 02-03-2013 03:32 PM

You can probably accomplish this with declare or even read

Code:

foo=bar
declare $foo=4
echo $bar

Code:

foo=bar
read $foo
echo $bar

It can also be done using eval, but I would strongly advise against that, because it's ugly and insecure.

I still think your best bet is to use arrays.

something like

Code:

read -a vars
will read the entire line in to an array, word per element. You can then iterate trough the array as

Code:

for i in "${vars[@]}"; do #iterating over the values
    echo "$i"
done

or

Code:

for i in "${!vars[@]}"; do #iterating over the indices
    echo "${vars[i]}"
done

This would work correctly regardless of the number of names in the input.

If you want to read the names, one per line, you can do something like

Code:

declare -a names

#read the number of elements
read number;

# read the names, one per line
for ((i = 0; i < number; i++)); do
    read -p "Enter value of name $i" names[i]
done

yet another way:

Code:

read -p "enter number of names: " number
echo "Enter $number names, one per line"
mapfile -n $number -t vars
echo "names entered:"
for i in "${vars[@]}"; do
  echo "$i"
done


soupmagnet 02-03-2013 06:00 PM

Quote:

Originally Posted by millgates (Post 4883758)
You can probably accomplish this with declare or even read
...

OH I SEE...

Agreed, that is much more effective than what I was trying to do. Thank you very much for your help.

konsolebox 02-04-2013 05:59 AM

Quote:

Originally Posted by soupmagnet (Post 4883629)
I'm trying to figure out a way to assign a value to a string of text, as it's created in the script, based on the user's input.

Associative arrays are the best solutions for that if you use Bash 4.0+.

David the H. 02-04-2013 01:22 PM

+1 to konsolebox. I was thinking the same thing.

How can I use array variables?
http://mywiki.wooledge.org/BashFAQ/005

The reason being that trying to create and use dynamic variable names is difficult and non-transparent.

How can I use variable variables (indirect variables, pointers, references) or associative arrays?
http://mywiki.wooledge.org/BashFAQ/006

Associative arrays let you use the same base array name, with a separate index for each string given. This means there's no need to worry about the limitations on variable names. They're much cleaner, safer, and easier to use than indirect variable creation.

Just be sure to declare the array globally, and not inside the function, if the values need to be used outside of it (although bash 4.2 has a new -g option to declare that you can use instead).

In addition:
Code:

for i in `myfunction`; do
Don't Read Lines With For!!

This is an incredibly common mistake. A for loop is for iterating over a list of words. But if there's whitespace involved then you cannot safely generate a list with a command substitution like this, due to the shell's word-splitting. You need to use a while+read loop or some other technique.

PS: $(..) is highly recommended over `..`

PPS: External tools like tr and sed can often be replaced by bash's built-in string manipulation features.


All times are GMT -5. The time now is 08:40 PM.