PDA

View Full Version : Bash question: piecing together variables



Noahdj
August 2nd, 2009, 11:42 AM
I am completely new to both linux, ubuntu, and BASH. I'm trying to create a simple program and was doing okay until the past 4 hours. For the life of me I can't find a solution...

I'm trying to refer to a variable list in a loop. My program starts by asking for a list of words:


#!/bin/bash

WDNUM=0
WORKAROUND=1
until [ $WORKAROUND = 0 ]; do
let WDNUM+=1
echo "Enter word #"$WDNUM", or type 0:"
read WORKAROUND
export WD$WDNUM=$WORKAROUND
doneonce I'm done entering a list of words, I'd like to echo the list I've typed. This is what I tried:


NOTIFY=1
WDPROCESS=1
until [ $NOTIFY = 0 ]; do
echo WD$WDPROCESS
WORD=$WD2
NOTIFY=$WORD
let WDPROCESS+=1
done

exitthe idea behind the line [echo WD$WDPROCESS] is that the first iteration would be [echo WD1], then [echo WD2] etc. but instead of treating the WD$WDPROCESS as a single variable, it treats it as the value of $WDPROCESS concatenated to the characters WD.

So when I run it, this is what I get:
Enter word #1, or type 0:
one
Enter word #2, or type 0:
0
WD1

Where "WD1" is output, I want to get "one."

Could somebody tell me how to declare the string as a single variable? Eventually I am going to try to replace the echo with a function, where I would have the same problem.

A similar issue I had was trying to piece variables together to assign new variables like so:
a=one
b=TWO
c=three
d=FOUR
$a$b=$c$d
#where $a$b=$c$d is equivalent to oneTWO=threeFOUR

Obviously I don't know what I'm doing. Any help appreciated.

Tek-E
August 2nd, 2009, 12:36 PM
which segment of code is your script???

lswb
August 2nd, 2009, 02:24 PM
I'm not really sure what you're trying to do, but perhaps search for "indirection" in the bash man page and see it that helps.

sisco311
August 2nd, 2009, 02:31 PM
use an array.

http://tldp.org/LDP/abs/html/arrays.html

Habbit
August 2nd, 2009, 03:46 PM
You need to use indirect references: instead of "echo WD$WDPROCESS", you need to use "eval echo \$WD$WDPROCESS". Note the escaped $ for the final expansion: the first (pre-eval) expansion substs WDPROCESS, then the shell executes "eval echo \$WD1", which is evaluated to "echo $WD1", which prints "one".

stroyan
August 2nd, 2009, 06:22 PM
You need to use the eval builtin to do indirect variable assignment.
But you can use either eval or ${!VAR} for reading indirect variable values.
Here are examples of assigning to and reading from an indirect variable
and an array element.


N=3
VALUE=three
NAME=WD$N
eval $NAME="$VALUE"
echo $NAME
echo "${!NAME}"
A[$N]=$VALUE
echo "${A[$N]}"

There are more flexible ways to loop over the elements of an array or of a set of variable names with a common prefix.
You can get a list of array index values with ${!A[@]}.
You can get a list variable names matching a prefix with ${!prefix@}.


for INDEX in ${!A[@]}; do echo $INDEX ${A[$INDEX]};done
for NAME in ${!WD@}; do echo $NAME ${!NAME};done

Noahdj
August 2nd, 2009, 10:50 PM
Wow, thanks everyone. I never expected so many replies so quickly. Sorry if my question was confusing, I only decided to ask after it had gotten to 3am.

I used the eval with \ to accomplish my goal. I'm sure my program is not very elegant, but it now works for my design. The finished code follows:


#!/bin/bash

WDNUM=0
WORKAROUND=1
until [ $WORKAROUND = 0 ]; do
let WDNUM+=1
echo "Enter word #"$WDNUM", or type \"0\" to finish:"
read WORKAROUND
export WD$WDNUM=$WORKAROUND
done

function repeater {
echo $1
}

echo ""; echo "###################### START ######################"

CHECK=1
until [ $CHECK = 0 ]; do
eval repeater \$WD$WDPROCESS
let WDPROCESS+=1
eval CHECK=\$WD$WDPROCESS
done

echo ""; echo "###################### END ######################"
exitThis is an example of me using it:


Enter word #1, or type "0" to finish:
Hello
Enter word #2, or type "0" to finish:
Ubuntu
Enter word #3, or type "0" to finish:
Forum
Enter word #4, or type "0" to finish:
0

###################### START ######################

Hello
Ubuntu
Forum

###################### END ######################I have yet to learn the code in the other replies, which I'll do now. Thank you all so much.

BTW, could anybody hint how I could place the "read" inline with the "Enter word..."?

And/or how do I accept read input with spaces?
This is what happens now:

Enter word #1, or type "0" to finish:
hello world
tester.sh: line 5: [: too many arguments

stroyan
August 3rd, 2009, 03:31 AM
You can use read -p to specify a prompt string instead of using echo.
You will need to add quoting around several instances of $VAR to handle spaces.
Here is a diff of the changes you would make to use read -p and more quoting.


--- t.sh 2009-08-02 20:22:13.000000000 -0600
+++ t2.sh 2009-08-02 20:29:39.000000000 -0600
@@ -2,11 +2,10 @@

WDNUM=0
WORKAROUND=1
-until [ $WORKAROUND = 0 ]; do
+until [ "$WORKAROUND" = 0 ]; do
let WDNUM+=1
-echo "Enter word #"$WDNUM", or type \"0\" to finish:"
-read WORKAROUND
-export WD$WDNUM=$WORKAROUND
+read -p "Enter word #$WDNUM, or type \"0\" to finish:" WORKAROUND
+export WD$WDNUM="$WORKAROUND"
done

function repeater {
@@ -16,8 +15,8 @@
echo ""; echo "###################### START ######################"

CHECK=1
-until [ $CHECK = 0 ]; do
-eval repeater \$WD$WDPROCESS
+until [ "$CHECK" = 0 ]; do
+eval repeater \"\$WD$WDPROCESS\"
let WDPROCESS+=1
eval CHECK=\$WD$WDPROCESS
done

Noahdj
August 3rd, 2009, 05:27 AM
Thank you Stroyan, that worked perfectly. The diff was a very informative way of showing me. LOL, I had to look up what diff meant before I could even start.

All my questions have been satisfied, and my cheesy little program is exactly how I wanted it. This is so much fun! Thanks to all for putting up with someone with two days of programming experience.

Finally I get to start work on the actual function.