LinuxQuestions.org
Latest LQ Deal: Latest LQ Deals
Home Forums Tutorials Articles Register
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 07-15-2011, 09:10 AM   #1
Dick Dastardly
LQ Newbie
 
Registered: Jul 2011
Posts: 23

Rep: Reputation: Disabled
(BASH) How to keep variable in scope?


So i've just started to learn about functions in Bash scripting.
I'm able to set the functions and execute the commands correctly. However, if my_var is set in the first function and then later in the script in the script the 2nd function is called, it doesn't seem to remember my_var and quits (at least i suspect this is the problem).

Here's my code (it requires yad available via webupd8. org).

My specific problem seems to lay in line #27 where if we view the changelog and then exit that window, it returns to the "main" function but any subsequent commands cause a crash. Is this because of the get command on line #29? It's presumably now out of scope after calling menu on line #25?

Sorry if this seems a convoluted explanation & thanks for any enlightenment
 
Old 07-15-2011, 09:27 AM   #2
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Arch + Xfce
Posts: 6,852

Rep: Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037
Remember that scripts operate in a linear fashion, evaluating each line in turn from first to last.

Your second call to menu happens inside the case statement, and when it exits, the script is now past the case statement, so there's nothing there to evaluate the output.

Put the menu and case test inside a while true loop instead, so that it continually loops over the menu. Then use the break command to exit the loop when you're ready to continue on.

Edit: In fact, I would put the subcommands inside functions as well, and call those instead, rather than embed them directly inside the case statement. It should help to keep your code more readable.

Last edited by David the H.; 07-15-2011 at 09:31 AM.
 
1 members found this post helpful.
Old 07-15-2011, 09:34 AM   #3
Dick Dastardly
LQ Newbie
 
Registered: Jul 2011
Posts: 23

Original Poster
Rep: Reputation: Disabled
Thanks David. It'll take me a while to make sense of that (haven't worked with while loops yet, the code that includes the while command was a hack for the zenity progress box i pinched). I'll let you know how it goes.

Last edited by Dick Dastardly; 07-15-2011 at 09:52 AM.
 
Old 07-15-2011, 09:43 AM   #4
Dick Dastardly
LQ Newbie
 
Registered: Jul 2011
Posts: 23

Original Poster
Rep: Reputation: Disabled
That works just fine, thank you David.
Here's the revised code:
Easy when you know how!
(can a moderator please remove my above post)

Last edited by Dick Dastardly; 07-15-2011 at 10:15 AM.
 
Old 07-15-2011, 10:24 AM   #5
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Arch + Xfce
Posts: 6,852

Rep: Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037
Yes, that's much cleaner.

Run your menu like this:
Code:
while true; do
   menu
   case $? in
        0) changelog ;;
	2) download ; break ;;  #or just exit, since it's the last command in the script
	*) exit ;;
   esac

done
This will re-run the menu until it hits "2", which breaks the loop, or any other non-zero number, which exits the script.

Since you know that the exit code will always be one of these numbers, there's no need to use any wildcards. I changed "1" to "*" though, just in case the yad command in the menu function could exit with another code (such as an error of some kind).

A couple of other suggestions:
Code:
sed -u 's/.*\ \([0-9]\+%\)\ \+\([0-9.]\+\ [KMB\/s]\+\)$/\1\n# Downloading \2/'
Kinda ugly. You can make it much more readable by using the -r option, and changing the delimiter to another character. You can also make it more efficient by targeting only the line you want. I think this will work...
Code:
sed -urn '\|eta| s|.*([0-9]+%).*([0-9]+[KMB]/s).*|\1\n# Downloading \2|p'
Awk may actually be better suited to this job, but I haven't tested it yet.

Code:
while [ $running -eq 0 ] ; do
When testing numeric conditions, it's advisable to use ((..)) instead.

Code:
while (( running == 0 )) ; do
http://mywiki.wooledge.org/ArithmeticExpression

Also consider using [[ when doing string comparisons or complex tests.

http://mywiki.wooledge.org/BashFAQ/031

And one more, that's more of question. Why are you using multiple commands that do about the same thing? wget and curl are similar, and yad is just a fork of zenity. Why not stick with just one or the other?

Edit: I see you figured it out yourself. Good job!

(No need to bother the mods; there's nothing wrong with your previous post.)

Last edited by David the H.; 07-15-2011 at 10:26 AM.
 
2 members found this post helpful.
Old 07-15-2011, 10:47 AM   #6
Dick Dastardly
LQ Newbie
 
Registered: Jul 2011
Posts: 23

Original Poster
Rep: Reputation: Disabled
Thanks for all the tips!
I use yad as it allows for multiple buttons, is slightly better looking and more functional.
I use curl to obtain the latest build number of chromium. And wget to download said build in .zip format. Should i be doing it another way? Would one command do for both? Why not use both?
The sed command is only so the zenity progress window stays open for the duration of the download (i'll confess to not knowing exactly what it does). It apparently won't work otherwise.

Also, what's the difference between download ; break ;; and download && break ;;?

Last edited by Dick Dastardly; 07-15-2011 at 10:59 AM.
 
Old 07-15-2011, 11:41 AM   #7
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Arch + Xfce
Posts: 6,852

Rep: Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037
No, there's nothing really wrong with it, it just seems a bit inefficient for a single script to have two dependencies when one would do. At the very least I'll bet you could change "zenity" to "yad" and it would likely still work; I doubt the syntax has changed that much in the fork.

Converting the wget command to curl may or may not be an option, since you'd have to convert the whole progress bar command. But you should be able to change "curl -s" to "wget -q -O-" instead to grab the contents of the LATEST file.

What the sed command is doing is taking the running progress output of the wget download and extracting the percentage/speed information from it, which it then feeds to the zenity dialog. Since wget prints the dialog to stderr, which doesn't pass through pipes (at least by default), there's a 2>&1 redirection from stderr to stdout, which allows sed to parse the output.

Last edited by David the H.; 07-15-2011 at 11:42 AM.
 
1 members found this post helpful.
Old 07-15-2011, 12:14 PM   #8
grail
LQ Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 10,007

Rep: Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191
Quote:
Also, what's the difference between download ; break ;; and download && break ;;?
The semicolon will allow the next command to run irrelevant of what happens with the previous one. && says to only run the second command if the first completes
successfully.

May I also suggest some alterations to assist:

1. Your entire get() function could just be:
Code:
get()
{
    wget --progress=bar:force "http://build.chromium.org/f/chromium/snapshots/Linux/$1/chrome-linux.zip" 2>&1 | zenity --text="Downloading Chromium ..." --progress --pulsate --auto-close --auto-kill
}
2. wget instead of curl (your choice of course)
Code:
latest=$(wget -q http://build.chromium.org/f/chromium/snapshots/Linux/LATEST -O-)
#and
wget -q http://build.chromium.org/f/chromium/snapshots/Linux/$latest/changelog.xml -O chromium_changelog.xml
3. Now this is a personal one, but as 'latest' is effectively a global variable, I would declare it either at the top of your program or prior to the while loop at the bottom, ie like the
start of your main:
Code:
declare latest
This can help you when you look at this code a year from now and have extended it by several hundred lines and ask yourself if the value of 'latest' is supposed to be available everywhere.

The alternative of course is to simply pass the value to the function (which I like a lot more). In this case I would move the call to curl / wget that sets latest to be prior to the
while at the bottom and pass it to the functions that require it.

4. There is no need to run a command and then est its return value with [] as if itself is a test:
Code:
get $latest
if [ $? -eq 0 ] ; then

# can simply be

if get $latest ; then
Same for unzip

Hope some of that helps
 
2 members found this post helpful.
Old 07-15-2011, 12:51 PM   #9
Dick Dastardly
LQ Newbie
 
Registered: Jul 2011
Posts: 23

Original Poster
Rep: Reputation: Disabled
A lot of info to digest! I will tinker with the script and let you guys know. Thanks.

EDIT:

OK, made some modifications.
Both of the suggestions for the get function failed.
Changed the if statements to remove obsolete code.
Replaced curl with wget in all instances.
yad simply won't work nicely with the progress box and zenity won't allow custom buttons so unfortunately will continue with the 2.

Big thanks for all the help and advice.

Last edited by Dick Dastardly; 10-05-2011 at 11:19 AM.
 
  


Reply



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
Lighttpd variable scope hydraMax Linux - Server 0 03-15-2011 09:39 AM
Variable scope in KSH v5.2.14 99/07/13.2 UltramaticOrange Programming 4 08-20-2008 10:41 AM
How to reference a variable outside it's block scope in C daYz Programming 8 10-03-2007 02:48 AM
Variable scope Ephracis Programming 5 07-28-2006 01:22 PM
Variable available scope problem ArthurHuang Programming 1 05-22-2006 12:16 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

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

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
Open Source Consulting | Domain Registration