LinuxQuestions.org
Support LQ: Use code LQ3 and save $3 on Domain Registration
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-03-2009, 07:31 AM   #1
JohnE1
Member
 
Registered: Jun 2007
Location: Houston, TX
Distribution: Fedora, CentOS, Ubuntu
Posts: 66

Rep: Reputation: 16
Question Using a variable to control a BASH for loop


I know of 4 different ways to use a for loop:

1. for I in {1..10}; do echo $I; done|

2. for I in 1 2 3 4 5 6 7 8 9 10; do echo $I; done|

3. for I in $(seq 1 10); do echo $I; done|

4. for ((I=1; I <= 10 ; I++)); do echo $I; done

I have a script which uses the 1st form of for loop. I'm trying to modify it to use a variable instead of a static hard-coded value in the section that controls the looping.of the for loop.

I've tried all different ways of quoting and escaping the variable, and the problem is that the quoting chars and escape char are being translated and passed into the loop along with the value stored in the variable.

For example, to change the start value of 1 to whatever value I want passed in through a variable:

Change: for i in {1..100}; do <something>; done

to: for i in {$a..10}; do <something>; done

I have tried: {{$a}..10} and {`$a`..10}, to have the variable evaluated first.
I have tried using the eval() function.
I have tried single and double quotes and the backslash escape character.

Nothing I've tried works. It's probably a syntax error, but as of now, I'm baffled.

Can anyone shed some light on the problem and solution, please? Thanks!

Last edited by JohnE1; 07-03-2009 at 07:33 AM.
 
Old 07-03-2009, 07:58 AM   #2
konsolebox
Senior Member
 
Registered: Oct 2005
Distribution: Gentoo, Slackware, LFS
Posts: 2,243
Blog Entries: 15

Rep: Reputation: 233Reputation: 233Reputation: 233
i don't really know what you mean but perhaps is it something like this?:

Code:
j=$(( RANDOM % 10 ))
for (( i = j; i <= 10; i++ )); do
    : do something
done
 
Old 07-03-2009, 08:00 AM   #3
pixellany
LQ Veteran
 
Registered: Nov 2005
Location: Annapolis, MD
Distribution: Arch/XFCE
Posts: 17,802

Rep: Reputation: 728Reputation: 728Reputation: 728Reputation: 728Reputation: 728Reputation: 728Reputation: 728
Interesting---it seems that the stuff in braces gets expanded before the variable is evaluated. I can't find a way around it.

Have you looked in ABS? (Advanced Bash Scripting Guide---on tldp.org)
 
Old 07-03-2009, 08:06 AM   #4
colucix
Moderator
 
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,452

Rep: Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941
The problem is that the "brace expansion" is performed before the "variable expansion". See here for details about shell expansion, where they are listed in the order that they are expanded. Hence you have to use eval to let the bash do the variable substitution and then expand the resulting "extended brace" {1..10}. This should work
Code:
#!/bin/bash
start=1
for i in $(eval echo {$start..10})
do
  echo $i
done
otherwise use the forms 3 or 4:
Code:
#!/bin/bash
start=1
for i in $(seq $start 10)
do
  echo $i
done
for (( i = $start; i <= 10 ; i++ ))
do
  echo $i
done

Last edited by colucix; 07-03-2009 at 08:07 AM. Reason: Forgotten the link...
 
Old 07-03-2009, 08:20 AM   #5
pixellany
LQ Veteran
 
Registered: Nov 2005
Location: Annapolis, MD
Distribution: Arch/XFCE
Posts: 17,802

Rep: Reputation: 728Reputation: 728Reputation: 728Reputation: 728Reputation: 728Reputation: 728Reputation: 728
Yep!!

Here's the quote from ABS..
Quote:
# Unfortunately, brace expansion does not lend itself to parameterization.
var1=1
var2=5
echo {$var1..$var2}
 
Old 07-03-2009, 08:34 AM   #6
konsolebox
Senior Member
 
Registered: Oct 2005
Distribution: Gentoo, Slackware, LFS
Posts: 2,243
Blog Entries: 15

Rep: Reputation: 233Reputation: 233Reputation: 233
following colucix's code, you can also save it in an array if you don't want to summon a subshell like:
Code:
eval "array=({${start}..10})"
eval "set -- {${start}..10}"
and do
Code:
for a in "${array[@]}"; do ...
for a in "$@"; do ...
Unless brace expansion does not work within array assignments, the code above that assigns varlues for an array should work. Sorry i don't have access to a terminal now .

Last edited by konsolebox; 07-03-2009 at 08:38 AM. Reason: clarity
 
Old 07-03-2009, 08:52 AM   #7
colucix
Moderator
 
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,452

Rep: Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941Reputation: 1941
Quote:
Originally Posted by konsolebox View Post
Unless brace expansion does not work within array assignments, the code above that assigns values for an array should work. Sorry i don't have access to a terminal now .
Yes, it works. Good tip!
 
Old 07-03-2009, 10:59 AM   #8
H_TeXMeX_H
Guru
 
Registered: Oct 2005
Location: $RANDOM
Distribution: slackware64
Posts: 12,928
Blog Entries: 2

Rep: Reputation: 1269Reputation: 1269Reputation: 1269Reputation: 1269Reputation: 1269Reputation: 1269Reputation: 1269Reputation: 1269Reputation: 1269
But option # 3 above works, why do you have to use the expansion method ? Just to make it harder ?
 
Old 07-03-2009, 11:41 AM   #9
pixellany
LQ Veteran
 
Registered: Nov 2005
Location: Annapolis, MD
Distribution: Arch/XFCE
Posts: 17,802

Rep: Reputation: 728Reputation: 728Reputation: 728Reputation: 728Reputation: 728Reputation: 728Reputation: 728
If I understand this correctly, the issue with the expansion is that the expansion happens before the parameter substitution. Thus
{$start...7}
does not get expanded correctly.

Why then does the array method work?
 
Old 07-03-2009, 11:55 AM   #10
konsolebox
Senior Member
 
Registered: Oct 2005
Distribution: Gentoo, Slackware, LFS
Posts: 2,243
Blog Entries: 15

Rep: Reputation: 233Reputation: 233Reputation: 233
Quote:
Originally Posted by pixellany View Post
Why then does the array method work?
simply put it that brace expansion does not parse variables and only raw data therefore we have to make the line *reappear* so that the variable will turn into a raw data:

if this works:
Code:
a=({1..10})
then evaluating the variable first then reparsing the line should work:
if a = 1:
Code:
eval "array=({$a..10})"
is just the same as
Code:
array=({1..10})
by the way
Quote:
{$start...7}
shouldn't that have 2 dots only?
 
Old 07-03-2009, 12:49 PM   #11
pixellany
LQ Veteran
 
Registered: Nov 2005
Location: Annapolis, MD
Distribution: Arch/XFCE
Posts: 17,802

Rep: Reputation: 728Reputation: 728Reputation: 728Reputation: 728Reputation: 728Reputation: 728Reputation: 728
Got it!!!---I had not appreciated exactly what "eval" does.

2 dots--of course. I was typing in a dark room.
 
Old 07-03-2009, 02:20 PM   #12
JohnE1
Member
 
Registered: Jun 2007
Location: Houston, TX
Distribution: Fedora, CentOS, Ubuntu
Posts: 66

Original Poster
Rep: Reputation: 16
Post

Quote:
Originally Posted by H_TeXMeX_H View Post
But option # 3 above works, why do you have to use the expansion method ? Just to make it harder ?
Good question and good point; so, let me give a good reply.

I already have working scripts using form #1 'for' loops. That was the main reason to try and stick with it.

Other related reasons are:

1. A minor edit would be simple to do globally using Vim,

2. I want to understand the inner workings of BASh;

I've never been one to program 'by example' alone.

Easy maintenance and gaining additional knowledge were good enough reasons for me, but NOT to make it harder.

I never said I wasn't willing, or prepared, to switch to one of the other forms of the 'for' loop.

Hope that clears it up.
 
Old 07-03-2009, 02:23 PM   #13
JohnE1
Member
 
Registered: Jun 2007
Location: Houston, TX
Distribution: Fedora, CentOS, Ubuntu
Posts: 66

Original Poster
Rep: Reputation: 16
Thumbs up

Quote:
Originally Posted by colucix View Post
The problem is that the "brace expansion" is performed before the "variable expansion". See here for details about shell expansion, where they are listed in the order that they are expanded. Hence you have to use eval to let the bash do the variable substitution and then expand the resulting "extended brace" {1..10}. This should work
Code:
#!/bin/bash
start=1
for i in $(eval echo {$start..10})
do
  echo $i
done
otherwise use the forms 3 or 4:
Code:
#!/bin/bash
start=1
for i in $(seq $start 10)
do
  echo $i
done
for (( i = $start; i <= 10 ; i++ ))
do
  echo $i
done
Thanks for all that great info, colucix!

That really helps alot.
 
Old 07-03-2009, 02:34 PM   #14
JohnE1
Member
 
Registered: Jun 2007
Location: Houston, TX
Distribution: Fedora, CentOS, Ubuntu
Posts: 66

Original Poster
Rep: Reputation: 16
Thumbs up

Quote:
Originally Posted by pixellany View Post
Yep!!

Here's the quote from ABS..
Thanks for looking that up and sharing it, pixellany (moderator).
 
Old 07-03-2009, 02:50 PM   #15
H_TeXMeX_H
Guru
 
Registered: Oct 2005
Location: $RANDOM
Distribution: slackware64
Posts: 12,928
Blog Entries: 2

Rep: Reputation: 1269Reputation: 1269Reputation: 1269Reputation: 1269Reputation: 1269Reputation: 1269Reputation: 1269Reputation: 1269Reputation: 1269
Well, technically I tend to use # 3 because it's more versatile, and you won't have problems like this one. If I were writing scripts that I would have to update later ... that would be the one I'd pick. And, of course, I don't disagree finding out why # 1 causes problems, that helps to understand the inner workings, but in the end I wouldn't use that option.
 
  


Reply

Tags
bash, loop, script, 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


Similar Threads
Thread Thread Starter Forum Replies Last Post
how to loop over text file lines within bash script for loop? johnpaulodonnell Linux - Newbie 8 04-05-2011 09:18 AM
BASH hangman: loop which adds the character to the variable if it matches uzi85 Programming 3 03-28-2009 09:31 AM
bash - loop over variable array names talanis Programming 2 02-19-2009 11:09 AM
bash/sh: global or local variable with for (loop) isssue frenchn00b Programming 9 11-06-2008 07:24 AM
bash loop within a loop for mysql ops br8kwall Programming 10 04-30-2008 03:50 AM


All times are GMT -5. The time now is 07:33 PM.

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