6

I want to use a string to control a for loop in bash. My first test code produces what I would expect and what I want:

$ aa='1 2 3 4'
$ for ii in $aa; do echo $ii; done
1
2
3
4

I'd like to use something like the following instead. This doesn't give the output I'd like (I can see why it does what it does).

$ aa='1..4'
$ for ii in $aa; do echo $ii; done
1..4

Any suggestions on how I should modify the second example to give the same output as the first?

Thanks in advance for any thoughts. I'm slowly learning bash but still have a lot to learn.

Mike

1
  • 3
    Why do you want to use a string to create a sequence of numbers? Can you give an example where for ((i=1; i<5; i++)); do …; done is insufficient? Commented Feb 12, 2015 at 20:45

4 Answers 4

2

The notation could be written out as:

for ii in {1..4}; do echo "$ii"; done

but the {1..4} needs to be written out like that, no variables involved, and not as the result of variable substitution. That is brace expansion in the Bash manual, and it happens before string expansions, etc. You'll probably be best off using:

for ii in $(seq 1 4); do echo "$ii"; done

where either the 1 or the 4 or both can be shell variables.

Sign up to request clarification or add additional context in comments.

2 Comments

The use of seq makes me question the whole string thing, since an arithmetic for loop is almost certainly better.
That's another way to do it, @kojiro, for sure. It might be better. I still tend to use shell notations that are not exclusive to Bash, but the question is tagged bash so you could provide that as an alternative answer.
2

You could use seq command (see man seq).

$ aa='1 4'
$ for ii in $(seq $aa); do echo $ii; done

1 Comment

This does not accept a string as input as described by the question.
1

Bash won't do brace expansion with variables, but you can use eval:

$ aa='1..4'
$ for ii in $(eval echo {$aa}); do echo $ii; done
1
2
3
4

You could also split aa into an array:

IFS=. arr=($aa) 
for ((ii=arr[0]; ii<arr[2]; ii++)); do echo $ii; done

Note that IFS can only be a single character, so the .. range places the numbers into indexes 0 and 2.

Comments

0

Note There are certainly more elegant ways of doing this, as Ben Grimm's answer, and this is not pure bash, as uses seq and awk.

One way of achieving this is by calling seq. It would be trivial if you knew the numbers in the string beforehand, so there would be no need to do any conversion, as you could simple do seq 1 4 or seq $a $b for that matter.

I assume, however, that your input is indeed a string in the format you mentioned, that is, 1..4 or 20..100. For this purpose you could convert the string into 2 numbers ans use them as parameters for seq.

One of possibly many ways of achieving this is:

$ `echo "1..4" | sed -e 's/\.\./ /g' | awk '{print "seq", $1, $2}'`
1
2
3
4

Note that this will work the same way for any input in the given format. If desired, sed can be changed by tr with similar results.

$ x="10..15"
$ `echo $x | tr "." " " | awk '{print "seq", $1, $2}'`
10
11
12
13
14
15

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.