LinuxQuestions.org
Help answer threads with 0 replies.
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
 
Search this Thread
Old 09-20-2009, 07:20 PM   #1
DrLove73
Senior Member
 
Registered: Sep 2009
Location: Srbobran, Serbia
Distribution: CentOS 5.5 i386 & x86_64
Posts: 1,118
Blog Entries: 1

Rep: Reputation: 129Reputation: 129
Unhappy Problem with getting array from function (bash)


Hi to all,

I have an problem with making changes to the array that is populated inside the function. I need to use it in main script and further functions.

I use CentOS 5.3 with bash 3.2.25.

Problem is when I finish "while read" multiple lines from grep output, I loose all changes made to the array menuitems (not menuitem which is local array).

How can I retrieve that array from function, or write directly to global array so element are not lost after the while loop?


Code:
#!/bin/bash


declare -a menuitems

LoadMenus () {
echo "Menu's:"
echo ""

OLD_IFS=$IFS # Store the original input field separator

declare -a tempmenuitem
declare -a menuitem

# Load all relevant data into array
string=$1
menuitem=($@)
echo "Local menuitem is "${menuitem[*]}

mojindex=0
totalindex=0
echo "Local menuitems is "${menuitems[*]}
echo "Local menuitem is "${menuitem[*]}

grep -e "menuitem," $UnitsDatabaseFile | while read menuline
do
   IFS=','
   # Load the comma separated fields into array
   tempmenuitem=(`echo "$menuline"`)
...
...
...
      menuitem[num]=${tempmenuitem[i]}
      menuitems[num]=${tempmenuitem[i]}
      echo -n "Item of menuitems $num je '" ${menuitem[num]} "'; "
   done
   echo ""   
   mojindex=$(($mojindex+1))

   echo "Local menuitem after is "${menuitem[*]}
   echo "Local tempmenuitem after is "${tempmenuitem[*]}
   echo "Local menuitems after is "${menuitems[*]} # So far menuitems has ~20 elements
done

# After while read loop, array menuitems is back to initial 3 elements
echo "Global menuitem after is "${menuitem[*]}
echo "Global menuitems after is "${menuitems[*]}

IFS="$OLD_IFS"
}

menuitems=(1 2 3)
echo "Menuitems before loading is "${menuitems[*]} # gives 1 2 3
LoadMenus ${menuitems[@]}
exit 0

Last edited by DrLove73; 09-20-2009 at 07:23 PM.
 
Old 09-20-2009, 09:32 PM   #2
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Debian sid + kde 3.5 & 4.4
Posts: 6,823

Rep: Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950
A function runs as a child process, and so all variables inside it are simply not available to the parent-level of the script. You'll need to redesign your function and the top-level command using it so that it's output sets a global array, or to otherwise generate the values you need directly.
 
Old 09-21-2009, 08:45 AM   #3
DrLove73
Senior Member
 
Registered: Sep 2009
Location: Srbobran, Serbia
Distribution: CentOS 5.5 i386 & x86_64
Posts: 1,118
Blog Entries: 1

Original Poster
Rep: Reputation: 129Reputation: 129
Ok, thanks. I solved it by creating temporary file that accepts grep output:
Code:
#!/bin/bash

declare -a menuitems

LoadMenus () {
echo "Menu's:"
echo ""

OLD_IFS=$IFS # Store the original input field separator

declare -a tempmenuitem

# Load all relevant data into array
mojindex=0
totalindex=0

grep -e "linux," $UnitsDatabaseFile > $TempDatabaseFile
exec 7< $TempDatabaseFile
while read -u 7 linuxline
do
   IFS=','
   # Load the comma separated fields into array
   tempmenuitem=(`echo "$menuline"`)
...
...
...
      menuitems[num]=${tempmenuitem[i]}
      echo -n "Item of menuitems $num je '" ${menuitem[num]} "'; "
   done
   echo ""   
   mojindex=$(($mojindex+1))
done
exec 7<&- # free file descriptor 7
echo "" > $TempDatabaseFile

echo "Global menuitems after is "${menuitems[*]}

IFS="$OLD_IFS"
}

LoadMenus

exit 0
Is there any better way (avoiding temp file?) in Bash, to grab just certain lines from file and feed them to the function as separate lines while been able to return them to parent process?
 
Old 09-21-2009, 02:09 PM   #4
slakmagik
Senior Member
 
Registered: Feb 2003
Distribution: Slackware
Posts: 4,113

Rep: Reputation: Disabled
Actually, with regard to functions:

Code:
:cat function
notachild() {
    var=set
}

notachild
echo parent_level: $var

:sh function
parent_level: set
which illustrates what the bash manual says:

Quote:
Functions are executed in the context of the current shell; no new process is created to interpret them
This is what the 'local' builtin is for - to restrict variables to their functions.

Your actual problem and some other ways around it in addition to your own: BashFAQ24
 
Old 09-21-2009, 02:24 PM   #5
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Debian sid + kde 3.5 & 4.4
Posts: 6,823

Rep: Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950
Huh. So I was wrong then. Functions don't run as subshells. I just tested it myself, and it looks like I wasn't just wrong in my first post, but completely wrong. Variables set in functions are modified globally, unless declared locally.

Sorry about that.

On the other hand, at least it does look like I was right that a subshell was to blame, since the loop in question was running inside a subprocess created by a pipe.

On the gripping hand, I should've noticed it. I've experienced that issue before myself.
 
Old 09-21-2009, 02:39 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,563
Blog Entries: 29

Rep: Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179
Try changing num to $num in
Code:
menuitems[num]=${tempmenuitem[i]}
There's no need to save and restore the IFS if you use this idiom
Code:
IFS=',' tempmenuitem=(`echo "$menuline"`)
It has the effect of setting IFS to ',' for the following (here assignment) command only.

There's no need for the echo in
Code:
tempmenuitem=(`echo "$menuline"`)
This will do what you want
Code:
IFS=',' tempmenuitem=( $menuline )
EDIT:

You can avoid having to use a temporary file like this
Code:
while read menuline
do
    <commands>
done < "$( grep -e "menuitem," $UnitsDatabaseFile )"
This replaces the pipe with redirection and so avoids the while loop being run in a sub-shell. The grep is run in a sub-shell but that's OK here -- it isn't setting any variables.

Last edited by catkin; 09-21-2009 at 02:44 PM.
 
Old 09-23-2009, 06:01 AM   #7
DrLove73
Senior Member
 
Registered: Sep 2009
Location: Srbobran, Serbia
Distribution: CentOS 5.5 i386 & x86_64
Posts: 1,118
Blog Entries: 1

Original Poster
Rep: Reputation: 129Reputation: 129
Thanks catkin. When you lear from examples like: "you can do this in many ways" , you are bound to lack optimization.

As for while loop, done at the end expects filename, not text, so it give error that filename is too long, and no filename lika that

"/usr/local/bin/sv3multi-db: line 464: menuitem,1,0,a,SRB,showMenuG,0,0,0,0,0,0,0" where line 464 is function start, functionname()

Any idea how to solve this? lines from greped file are:
"menuitem,1,0,a,SRB,showMenuG,0,0,0,0,0,0,0"
 
Old 09-23-2009, 06:51 AM   #8
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,563
Blog Entries: 29

Rep: Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179
Quote:
Originally Posted by DrLove73 View Post
As for while loop, done at the end expects filename, not text, so it give error that filename is too long, and no filename lika that
What was I thinking?! Just ignore that nonsense. Sorry
 
Old 09-23-2009, 07:53 AM   #9
DrLove73
Senior Member
 
Registered: Sep 2009
Location: Srbobran, Serbia
Distribution: CentOS 5.5 i386 & x86_64
Posts: 1,118
Blog Entries: 1

Original Poster
Rep: Reputation: 129Reputation: 129
Thumbs up Populating array inside a loop with Multiple-line text [SOLVED]

Ok, I solved my problem after lurking in Advanced Bash Shell Scripting Guide with this:

Code:
#!/bin/bash

declare -a menuitems

LoadMenus () {
echo "Menu's:"
echo ""

OLD_IFS=$IFS # Store the original input field separator

declare -a tempmenuitem

# Load all relevant data into array
mojindex=0
totalindex=0

for linuxline in $( grep -e "menuitem," $UnitsDatabaseFile | sed -e '/#/d' ) 
do
   IFS=','
   # Load the comma separated fields into array
   tempmenuitem=( $menuline )
...
...
...
      menuitems[num]=${tempmenuitem[i]}
      echo -n "Item of menuitems $num je '" ${menuitem[num]} "'; "
   done
   echo ""   
   mojindex=$(($mojindex+1))
done
echo "Global menuitems after is "${menuitems[*]}

IFS="$OLD_IFS"
}

LoadMenus

exit 0
Stupidly simple (when you know HOW)!!!

Last edited by DrLove73; 09-23-2009 at 07:54 AM.
 
Old 09-23-2009, 08:05 AM   #10
DrLove73
Senior Member
 
Registered: Sep 2009
Location: Srbobran, Serbia
Distribution: CentOS 5.5 i386 & x86_64
Posts: 1,118
Blog Entries: 1

Original Poster
Rep: Reputation: 129Reputation: 129
Quote:
Originally Posted by catkin View Post
This will do what you want
Code:
IFS=',' tempmenuitem=( $menuline )
I am sorry I must say that this does not work only for one command, but for entire script. Maybe it works for one command/script when invoked from command line, but inside the script it wrecks havoc.

Thanks anyway for all of your help. On another forum (wireless stuff) I reached ~2,800 posts in 3 years (avg 2.5 posts per day) so I know that you need willpower to help, to provide any kind of answer, not to mention finding a correct answer with scarce input.

Last edited by DrLove73; 09-23-2009 at 09:28 AM.
 
Old 09-23-2009, 01:20 PM   #11
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,563
Blog Entries: 29

Rep: Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179
Quote:
Originally Posted by DrLove73 View Post
I am sorry I must say that this does not work only for one command, but for entire script. Maybe it works for one command/script when invoked from command line, but inside the script it wrecks havok.
Many thanks for pointing it out. Two serious mistakes in one post At least it made me look into the technique further and learn some more. See this post for the result.
 
Old 09-23-2009, 09:06 PM   #12
DrLove73
Senior Member
 
Registered: Sep 2009
Location: Srbobran, Serbia
Distribution: CentOS 5.5 i386 & x86_64
Posts: 1,118
Blog Entries: 1

Original Poster
Rep: Reputation: 129Reputation: 129
It is no problem really. Since I am new to BASH, I just add A LOT of echo commands for all relevant variables after every relevant block of code and just observe the output and adjust as needed.

Also, there is nothing wrong in expanding on your(our) knowledge every day we breathe, that is how we get smarter. A matter of fact, if you had not gave me the "While read" redirection example, I would not have been able to trace it to this "for loop" elegant solution for arrays from multiple-line text, so many thanks for that.
 
Old 09-23-2009, 11:24 PM   #13
slakmagik
Senior Member
 
Registered: Feb 2003
Distribution: Slackware
Posts: 4,113

Rep: Reputation: Disabled
Quote:
Originally Posted by DrLove73 View Post
I just add A LOT of echo commands for all relevant variables after every relevant block of code and just observe the output and adjust as needed.
If you've got a specific problem area, that can be very helpful but, if you're doing a lot of that, you can just stick an '-x' on the end of your shebang line or run the script as '(ba)sh -x script', possibly redirecting stderr to a file so you can look through that.
 
Old 09-23-2009, 11:25 PM   #14
chrism01
Guru
 
Registered: Aug 2004
Location: Sydney
Distribution: Centos 6.6, Centos 5.10
Posts: 16,324

Rep: Reputation: 2041Reputation: 2041Reputation: 2041Reputation: 2041Reputation: 2041Reputation: 2041Reputation: 2041Reputation: 2041Reputation: 2041Reputation: 2041Reputation: 2041
You could just put

set -xv

as the 2nd line of your code. It shows everything before and after substitutions etc as it goes.
Great for debugging.
 
Old 09-24-2009, 04:02 AM   #15
DrLove73
Senior Member
 
Registered: Sep 2009
Location: Srbobran, Serbia
Distribution: CentOS 5.5 i386 & x86_64
Posts: 1,118
Blog Entries: 1

Original Poster
Rep: Reputation: 129Reputation: 129
Wellllll, that is nice to know, but my current (combined) script has ~1000 lines and output definitely crosses that line. And just one of my arrays will fill ~100 rows on the screen when I populate database file. "Not everything that shines is made of gold...".

I put echo lines as I see fit, and then just comment them out when I need them, parts at the time. If I change something before that point, I just uncomment them out again until I am pleased with changes. It's a best way to go.

Thanks on the tip, I've save it for future use.

P.S. I have been programming in BASIC, DOS Batch files, Pascal/Delphi, VisualBasic/Access, SQL code, and finaly BASH since 1993-1994. On and off of course, but let's say I have been arround.

Last edited by DrLove73; 09-24-2009 at 04:08 AM.
 
  


Reply


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


Similar Threads
Thread Thread Starter Forum Replies Last Post
create a global associative array variable inside a function of a bash script konsolebox Programming 3 07-14-2009 07:08 AM
passing array and variable to function in bash script ajaypitroda Programming 2 07-08-2009 12:10 AM
Array problem in Bash basildon Linux - Newbie 2 09-09-2008 06:56 AM
Bash array to function rejeep Programming 5 11-27-2007 03:05 PM
MAJOR problem ... bash script array HELP !!!!! michael_util Slackware 1 02-13-2004 07:51 AM


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

Main Menu
Advertisement
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