LinuxQuestions.org
Share your knowledge at the LQ Wiki.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices

Reply
 
LinkBack Search this Thread
Old 12-14-2009, 02:47 PM   #1
GrapefruiTgirl
Guru
 
Registered: Dec 2006
Location: underground
Distribution: Slackware64
Posts: 7,594

Rep: Reputation: 542Reputation: 542Reputation: 542Reputation: 542Reputation: 542Reputation: 542
Lightbulb Basic Bash: How to use eval to evaluate variable names made of arbitrary strings.


And in addition to the exact subject of the title of this thread: "How to do arrays, without using real arrays."

Here's how to eval variable names, and get their contents, when the variable names are made of arbitrary strings:

Code:
#STEP 1 -- some arbitrary words to make some variable names from:
sasha@reactor:~$ var1='hello'
sasha@reactor:~$ var2='goodbye'
sasha@reactor:~$ var3='blah'
sasha@reactor:~$ var4='leopard'

#STEP 2 -- Form some strings from some of the arbitrary pieces:
sasha@reactor:~$ echo ${var1}_${var3}_${var2}
hello_blah_goodbye <--fresh string
sasha@reactor:~$ echo ${var1}_${var4}_${var2}
hello_leopard_goodbye <--fresh string
sasha@reactor:~$

#STEP 3 -- If those strings were actually variables...
sasha@reactor:~$ echo \$${var1}_${var3}_${var2} 
$hello_blah_goodbye <--like this
sasha@reactor:~$ echo \$${var1}_${var4}_${var2}
$hello_leopard_goodbye <--or like this
sasha@reactor:~$

#STEP 4 ...they would be empty variables; we haven't set them to anything:
sasha@reactor:~$ echo $hello_blah_goodbye

sasha@reactor:~$ echo $hello_leopard_goodbye

sasha@reactor:~$ 

#STEP 5 -- Give the new variables some values.. 
# If the value is multiple words (contains spaces) we must include "quotes" around the value: 
sasha@reactor:~$ eval ${var1}_${var3}_${var2}="'weird greeting'"
sasha@reactor:~$
# if there's no spaces, we don't need quotes:
sasha@reactor:~$ eval ${var1}_${var4}_${var2}='carnivore' <--quotes here optional
sasha@reactor:~$

#STEP 6 -- Now they aren't empty; to see the value of a variable, we use echo:
sasha@reactor:~$ echo $hello_blah_goodbye
weird greeting
sasha@reactor:~$ echo $hello_leopard_goodbye
carnivore
sasha@reactor:~$ 

#STEP 7 -- Create an echo command for the shell to execute, to return to us the value
# of the variable, when different combinations of our arbitrary words might comprise the variable name:
sasha@reactor:~$ echo "echo \$${var1}_${var3}_${var2}" <--quotes are optional, but safer!
echo $hello_blah_goodbye <-- a command for the shell to run
sasha@reactor:~$  echo echo \$${var1}_${var4}_${var2} <--no quotes, but still works
echo $hello_leopard_goodbye <-- another command for the shell to run
sasha@reactor:~$ 

# We can run those commands ourselves, like in STEP 6 above:
sasha@reactor:~$ echo $hello_blah_goodbye
weird greeting
sasha@reactor:~$ echo $hello_leopard_goodbye
carnivore
sasha@reactor:~$

#STEP 9 -- ..or the shell can run them for us (as if within a script) and give us the value.
# To get the value of a variable made of various arbitrary parts, we need eval
# to evaluate the parts and string them together:
sasha@reactor:~$ hi_and_goodbye=$(eval echo \$${var1}_${var3}_${var2})
sasha@reactor:~$ echo $hi_and_goodbye
weird greeting
# Or if we know the parts, we can be direct, and don't need eval:
sasha@reactor:~$ cat_thing=$(echo $hello_leopard_goodbye)
sasha@reactor:~$ echo $cat_thing
carnivore
sasha@reactor:~$
Now, Here's a little script (I actually just stuck the code into the console) using the above technology to illustrate simulating an array of 11 elements (for the purpose of using an array-like construct, in a shell environment that doesn't support arrays):
Code:
# The REAL ARRAY method:
# first, fill the array:
for i in $(seq 0 10); do
array[$i]="real array element $i here!"
done
# now, see the values of the array elements:
for i in $(seq 0 10); do
the_content="${array[i]}"
echo $the_content
done

# The NON-ARRAY method:
# be wary of using exclamation point in the fake element
# because bash tries to execute it as a history event. If you want
# to use ! in your fake array element, follow it with a newline (like I did here) or whatever.
# see man bash for details of the ! event operator.

# first, define the fake array's name:
fake_array='fake_array'
# fill the phony array:
for i in $(seq 0 10); do
eval ${fake_array}${i}="'phoney element $i here!
'"
done
# now, see the values of the 'phony array's elements:
for i in $(seq 0 10); do
the_content=$(echo $(eval echo \$$fake_array${i}))
echo $the_content
done
Here's the output in Bash shell:
Code:
# The REAL ARRAY method:
sasha@reactor:~$ for i in $(seq 0 10); do 
> array[$i]="real array element $i here"
> done
sasha@reactor:~$ for i in $(seq 0 10); do
> the_content="${array[i]}"
> echo $the_content
> done
real array element 0 here
real array element 1 here
real array element 2 here
real array element 3 here
real array element 4 here
real array element 5 here
real array element 6 here
real array element 7 here
real array element 8 here
real array element 9 here
real array element 10 here
sasha@reactor:~$ 

# The NON-ARRAY method:
sasha@reactor:~$ fake_array='fake_array'
sasha@reactor:~$ for i in $(seq 0 10); do
> eval ${fake_array}${i}="'phoney element $i here!
> '"
> done
sasha@reactor:~$ for i in $(seq 0 10); do
> the_content=$(echo $(eval echo \$$fake_array${i}))
> echo $the_content
> done
phoney element 0 here!
phoney element 1 here!
phoney element 2 here!
phoney element 3 here!
phoney element 4 here!
phoney element 5 here!
phoney element 6 here!
phoney element 7 here!
phoney element 8 here!
phoney element 9 here!
phoney element 10 here!
sasha@reactor:~$
And for comparison, here's the same procedures run in the Dash shell:
Code:
#The REAL ARRAY method:
\u@\h:\w$ for i in $(seq 0 10); do
> array[$i]="real array element $i here"
> done
dash: array[0]=real array element 0 here: not found
dash: array[1]=real array element 1 here: not found
dash: array[2]=real array element 2 here: not found
dash: array[3]=real array element 3 here: not found
dash: array[4]=real array element 4 here: not found
dash: array[5]=real array element 5 here: not found
dash: array[6]=real array element 6 here: not found
dash: array[7]=real array element 7 here: not found
dash: array[8]=real array element 8 here: not found
dash: array[9]=real array element 9 here: not found
dash: array[10]=real array element 10 here: not found
\u@\h:\w$ 

# no point trying to get the contents; it's obvious the array is non-existent.

#The FAKE ARRAY method:
\u@\h:\w$ fake_array='fake_array'
\u@\h:\w$ for i in $(seq 0 10); do
> eval ${fake_array}${i}="'phoney element $i here!
> '"
> done
\u@\h:\w$ for i in $(seq 0 10); do
> the_content=$(echo $(eval echo \$$fake_array${i}))
> echo $the_content
> done
phoney element 0 here!
phoney element 1 here!
phoney element 2 here!
phoney element 3 here!
phoney element 4 here!
phoney element 5 here!
phoney element 6 here!
phoney element 7 here!
phoney element 8 here!
phoney element 9 here!
phoney element 10 here!
\u@\h:\w$ 

# Success!
 
Old 12-15-2009, 08:28 AM   #2
gnashley
Amigo developer
 
Registered: Dec 2003
Location: Germany
Distribution: Slackware
Posts: 4,724

Rep: Reputation: 449Reputation: 449Reputation: 449Reputation: 449Reputation: 449
Brilliant Sasha -you must have spent some time on that...
 
Old 12-15-2009, 10:51 AM   #3
GrapefruiTgirl
Guru
 
Registered: Dec 2006
Location: underground
Distribution: Slackware64
Posts: 7,594

Original Poster
Rep: Reputation: 542Reputation: 542Reputation: 542Reputation: 542Reputation: 542Reputation: 542
Not sure about 'brilliant' but thank you Gilbert! I did think it was pretty clever though

What brought this on was this thread: http://www.linuxquestions.org/questi...script-775058/ where I'm thinking about considering trying to convert my large, "already-well-underway" project from "uses arrays" to "doesn't use arrays" so it can be (more) portable.

This sure would have been easier had it occurred to me a long time ago :/ because it's proving darned tricky to convert.. I made a little sed script that did/does a lot of the conversions on the real project for me -- the regexes are about a foot long on average -- and while it *works*, it doesn't catch every instance, so needs a bit of fine tuning..

Still ongoing!

Sasha
 
Old 12-15-2009, 12:08 PM   #4
gnashley
Amigo developer
 
Registered: Dec 2003
Location: Germany
Distribution: Slackware
Posts: 4,724

Rep: Reputation: 449Reputation: 449Reputation: 449Reputation: 449Reputation: 449
I have a similar dilemma with a shell project I've been adding to for 5 years now. It's up to over 10,000 lines, so major changes are difficult to implement. Actually I thought for a long time that I'd do the same thing -converting to POSIX, but in the end I went the other way and implemented more stuff in pure bash insetad of calling externals so much. The other sticky thing is that it would be nice to have it internationalized, but, the code is chick full of 'echo' so it would be a major undertaking to convert it to using gettext.
Anyway, thanks for your nice stuff there -I've saved it somewhere where I'm sure it won't get lost -errr, where it won't get deleted.
BTW, you, tuxdev and myself may be the most fanatic 'do it in shell' types around... IF you ever create pure-shell or pure-bash clones of any utilities, send me a copy to add to my collection:
http://distro.ibiblio.org/pub/linux/...ects/BashTrix/
 
Old 12-15-2009, 02:28 PM   #5
GrapefruiTgirl
Guru
 
Registered: Dec 2006
Location: underground
Distribution: Slackware64
Posts: 7,594

Original Poster
Rep: Reputation: 542Reputation: 542Reputation: 542Reputation: 542Reputation: 542Reputation: 542
RE `echo`

I thought/think that echo is portable, no? I realize that `echo -n` or `echo -e` are not portable, so I changed those in my project to "printf", which works great, despite that I needed to add \n to the end of a number of string messages that the program emits, because printf doesn't \n automatically...

I also found one chunk of stuff, a long multi-line wrapped string containing <ENTER> type of newlines and a load of backslashes, I wanted to echo to the screen, and while Bash has no trouble, Dash was interpreting \\ to mean \ and screwing up the output, regardless of using echo/printf/''/' . I ended up calling /bin/echo in that case, and using single quotes around the string. It now works fine in both shells.
 
Old 12-15-2009, 02:42 PM   #6
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Servers: Debian Squeeze and Wheezy. Desktop: Slackware64 14.0. Netbook: Slackware 13.37
Posts: 8,512
Blog Entries: 27

Rep: Reputation: 1174Reputation: 1174Reputation: 1174Reputation: 1174Reputation: 1174Reputation: 1174Reputation: 1174Reputation: 1174Reputation: 1174
Quote:
Originally Posted by gnashley View Post
BTW, you, tuxdev and myself may be the most fanatic 'do it in shell' types around...
I wrote a 10,000+ lines of ksh for a client's warehousing system (their choice of language!) and currently have a 2,000+ line bash backup script; does that count me in?
 
Old 12-15-2009, 02:54 PM   #7
GrapefruiTgirl
Guru
 
Registered: Dec 2006
Location: underground
Distribution: Slackware64
Posts: 7,594

Original Poster
Rep: Reputation: 542Reputation: 542Reputation: 542Reputation: 542Reputation: 542Reputation: 542
My gosh, 2000+ lines for a backup script!! That's 'mongous! Almost as large as my current project -- my little backup script is lucky if it tops a couple dozen lines, though it's just for my desktop machine.
 
Old 12-16-2009, 01:09 AM   #8
gnashley
Amigo developer
 
Registered: Dec 2003
Location: Germany
Distribution: Slackware
Posts: 4,724

Rep: Reputation: 449Reputation: 449Reputation: 449Reputation: 449Reputation: 449
catkin, you are (possibly) welcome in the club... I wasn't really referring to my 10,000 line script when i siad that, though. I meant pure-shell fanatics. I have it on experience that GrapefruiTgirl and tuxdev both try hard to do things with pure shell where others do it with perl/awk/sed. Check out the little project I linked to and you'll see what I meant -~14 clones of basic utilities which use no external programs at all, sort, cat, cut and even wget -all in pure bash. Anyway, I was trying to be exclusive or boast -I take a few potshots for doig things in shell.
 
Old 12-16-2009, 01:10 AM   #9
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Servers: Debian Squeeze and Wheezy. Desktop: Slackware64 14.0. Netbook: Slackware 13.37
Posts: 8,512
Blog Entries: 27

Rep: Reputation: 1174Reputation: 1174Reputation: 1174Reputation: 1174Reputation: 1174Reputation: 1174Reputation: 1174Reputation: 1174Reputation: 1174
Quote:
Originally Posted by GrapefruiTgirl View Post
My gosh, 2000+ lines for a backup script!! That's 'mongous! Almost as large as my current project -- my little backup script is lucky if it tops a couple dozen lines, though it's just for my desktop machine.
It has a lot of comments and vertical space and, AFAIK, it's just for my desktop machine. <shameless plug>MyBackup.sh 0.3 is downloadable from Denis Corbin, dar's creator samples page. It was developed and tested on ubuntu. Version 0.4 is being tested on SLackware 13.0.</shameless plug>
 
Old 12-16-2009, 10:25 AM   #10
GrapefruiTgirl
Guru
 
Registered: Dec 2006
Location: underground
Distribution: Slackware64
Posts: 7,594

Original Poster
Rep: Reputation: 542Reputation: 542Reputation: 542Reputation: 542Reputation: 542Reputation: 542
Solved!!! I've converted all the arrays in my script, and it's working like a charm. Since the several threads I've created in the last few days are related, see these 2 threads for reference:

http://www.linuxquestions.org/questi...52#post3793352
http://www.linuxquestions.org/questi...script-775058/

Technically, THIS here thread isn't a question, so I'm not marking IT solved. Comments are welcome from anyone.

Sasha
 
  


Reply

Tags
array, bash, dash, posix, shell


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off
Trackbacks are Off
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
bash - loop over variable array names talanis Programming 2 02-19-2009 11:09 AM
Evaluate strings as case-insensitive gchilders Linux - Newbie 4 10-13-2008 04:27 PM
BASH Using varaibles as variable names SwingingSimian Programming 5 09-02-2008 02:56 AM
Evaluate my Bash script for runaway proc RaelOM Programming 2 10-26-2007 09:30 AM
opening files with strings for file names veilig Programming 4 11-10-2003 11:19 AM


All times are GMT -5. The time now is 11:12 AM.

Main Menu
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration