Bash seq to produce two separate sequences of numbers in a for loop
Asked Answered
D

1

16

I would like to create a simple for bash loop that iterates over a sequence of numbers with a specific interval and then a different sequence of numbers, e.g.

for i in $(seq 0 5 15)
do
    echo $i
done

But after interating through i=0, 5, 10, 15, I'd like it to iterate through say 30, 35, 40, 45 as well.

Is there a way to do this using seq? Or an alternative?

Daciadacie answered 9/9, 2015 at 14:32 Comment(5)
The actual content of the for loop is more than just echo$i (about 200 lines) I don't want to repeat it and make my script huge!Daciadacie
You can put those 200 lines in a function and call it twice.Levey
Just seq 0 5 45 and check that it's not 20 or 25 at the top and continue if it is?Christoperchristoph
@Christoperchristoph I think your suggestion works for this example, but not in the general case (e.g. if the interval changes).Rotogravure
Note that seq should rarely, if ever, be used in a shell script. Most modern shells have more efficient alternatives (such as C-style for loops), and a script that requires POSIX compatibility cannot use seq because it is not defined by POSIX. (You would use a while loop and update the index yourself instead.)Yeaton
R
21

Approach 1

Simply augment the command within $(...) with another call to seq:

for i in $(seq 0 5 15; seq 30 5 45); do
    echo $i
done

and then

$ bash test.sh
0
5
10
15
30
35
40
45

# Approach 2

In your follow-up comment, you write

The actual content of the for loop is more than just echo$i (about 200 lines) I don't want to repeat it and make my script huge!

As an alternative to the approach outlined above, you could define a shell function for those 200 lines and then call the function in a series of for loops:

f() {
   # 200 lines
   echo $i
}

for i in $(seq 0 5 15) do
    f
done

for i in $(seq 30 5 45) do
    f
done

Approach 3 (POSIX-compliant)

For maximum portability across shells, you should make your script POSIX-compliant. In that case, you need have to eschew seq, because, although many distributions provide that utility, it's not defined by POSIX.

Since you can't use seq to generate the sequence of integers to iterate over, and because POSIX doesn't define numeric, C-style for loops, you have to resort to a while loop instead. To avoid duplicating the code related to that loop, you can define another function (called custom_for_loop below):

custom_for_loop () {
  # $1: initial value
  # $2: increment
  # $3: upper bound
  # $4: name of a function that takes one parameter
  local i=$1
  while [ $i -le $3 ]; do
      $4 $i
      i=$((i+$2))
  done
}

f () {
    printf "%s\n" "$1"
}

custom_for_loop 0 5 15 f
custom_for_loop 30 5 45 f
Rotogravure answered 9/9, 2015 at 14:35 Comment(4)
Also, for i in $(seq 0 5 15) $(seq 30 5 45) will work.Yeaton
I wouldn't bother trying to define a new command to wrap the loop, but the loop structure itself looks fine.Yeaton
@Yeaton Well, I agree that wrapping the loop in a function is overkill, here. However, if there were many similar loops to go through, it would make sense to abstract the loop away.Rotogravure
In between of sequences also can be put numbers: for i in $(seq 1 3) 5 $(seq 7 9) 11; do echo $i; doneGrus

© 2022 - 2024 — McMap. All rights reserved.