Run through two sequences in one loop
I'm trying to run through two sequences in the same loop in my shell like below:
#!/bin/bash
for i in (1..15) and (20..25) ;
do echo $i ...... .....other process
doneany idea how I can achieve this?
34 Answers
You only need brace expansion for that
$ for n in {1..3} {200..203}; do echo $n; done
1
2
3
200
201
202
203We can pass a list to for (for i in x y z; do stuff "$i"; done).
So here, braces { } get the shell to expand your sequences into a list. You only need put a space between them, since the shell splits lists of arguments on those.
Alternatively we can use seq (print a sequence of numbers), here are two equivalent examples:
for i in `seq 1 3` `seq 101 103`; do echo $i; donefor i in $(seq 1 3) $(seq 101 103); do echo $i; doneIf it is a script, for repetitive tasks, you can use functions:
#!/bin/bash
my_function() { echo "$1"; }
for i in {1..3}; do my_function "$i"; done
for i in {101..103}; do my_function "$i"; done#!/bin/bash
my_function() { for i in `seq $1 $2`; do echo "$i"; done; }
my_function "1" "3"
my_function "101" "103" Zanna's answer and pa4080's answer are both good and I'd probably go with one of them in most circumstances. Perhaps it goes without saying, but for the sake of completeness, I'll say it anyway: You can load each value into an array and then loop over the array. For example:
the_array=( 1 2 3 4 5 6 7 8 9 10 20 21 22 23 24 25 )
for i in "${the_array[@]}";
do echo $i
done 2 Looping without a loop
Zanna's answer is absolutely correct and well suited for bash, but we can improve that even more without utilizing a loop.
printf "%d\n" {1..15} {20..25}Behavior of printf is such that if the number of ARGUMENTS is greater than format controls in 'FORMAT STRING', then printf will split all ARGUMENTS into equal chunks and keep fitting them to the format string over and over until it runs out of ARGUMENTS list.
If we're striving for portability, we can utilize printf "%d\n" $(seq 1 15) $(seq 20 25) instead
Let's take this further and more fun. Say we want to perform an action rather than just printing numbers. For creating files out of that sequence of numbers, we could easily do touch {1..15}.txt {20..25}.txt. What if we want multiple things to occur? We could also do something like this:
$ printf "%d\n" {1..15} {20..25} | xargs -I % bash -c 'touch "$1.txt"; stat "$1.txt"' sh %Or if we want to make it old-school style:
printf "%d\n" {1..15} {20..25} | while read -r line; do touch "$line".txt; stat "$line".txt; rm "$line".txt;
donePortable but verbose alternative
If we want to make a script solution that works with shells that don't have brace expansion ( which is what {1..15} {20..25} relies on) , we can write a simple while loop:
#!/bin/sh
start=$1
jump=$2
new_start=$3
end=$4
i=$start
while [ $i -le $jump ]
do printf "%d\n" "$i" i=$((i+1)) if [ $i -eq $jump ] && ! [ $i -eq $end ];then printf "%d\n" "$i" i=$new_start jump=$end fi
doneOf course this solution is more verbose, some things could be shortened, but it works. Tested with ksh, dash, mksh, and of course bash.
Bash C-style loop
But if we wanted to make a loop bash-specific ( for whatever reason, perhaps not just printing but also doing something with those numbers ), we can also do this (basically C-loop version of the portable solution) :
last=15; for (( i=1; i<=last;i++ )); do printf "%d\n" "$i"; [[ $i -eq $last ]] && ! [[ $i -eq 25 ]] && { i=19;last=25;} ;doneOr in more readable format:
last=15
for (( i=1; i<=last;i++ ));
do printf "%d\n" "$i" [[ $i -eq $last ]] && ! [[ $i -eq 25 ]] && { i=19;last=25;}
donePerformance comparison of different looping approaches
bash-4.3$ time bash -c 'printf "%d\n" {0..50000}>/dev/null'
real 0m0.196s
user 0m0.124s
sys 0m0.028s
bash-4.3$ time bash -c 'for i in {1..50000}; do echo $i > /dev/null; done'
real 0m1.819s
user 0m1.328s
sys 0m0.476s
bash-4.3$ time bash -c ' i=0;while [ $i -le 50000 ]; do echo $i>/dev/null; i=$((i+1)); done'
real 0m3.069s
user 0m2.544s
sys 0m0.500s
bash-4.3$ time bash -c 'for i in $(seq 1 50000); do printf "%d\n" > /dev/null; done'
real 0m1.879s
user 0m1.344s
sys 0m0.520sNon-shell alternative
Just because we can here's Python solution
$ python3 -c 'print("\n".join([str(i) for i in (*range(1,16),*range(20,26))]))'Or with a bit of shell:
bash-4.3$ python3 << EOF
> for i in (*range(16),*range(20,26)):
> print(i)
> EOF 2