10

I was trying to make a very simple bash script to list all of the multiples of five between 375 and 3500 (375, 380, 385...). One thing that I tried and didn't work is:

for i in {375..3500}
do
        echo $i
        (($i += 5))
done

I gave up after a while and wrote this in BASIC in about 15 seconds:

10 count = 375
20 print count
30 count = count+5
40 if count < 3500 then goto 20

How can I make my BASIC program in a bash script?

1
  • this is better on stack overflow - Unix & Linux is about the OS, not coding. Coding is for stackoverflow. Jul 18, 2016 at 15:43

7 Answers 7

28

Since you use brace expansion anyway, so make use of its feature fully:

echo {375..3500..5}

You can also use this technique to print each number in separate line with optional text by using printf instead of echo, for example:

$ printf "Number %s is generated.\n" {375..3500..5}
Number 375 is generated.
Number 380 is generated.
Number 385 is generated.
...

Edit

As pointed out by @kojiro in the comment Mac OS uses bash 3 as the default shell, which doesn't support increment in sequence expression of brace expansion. You need to upgrade to bash version 4 or use other shell which supports that (e.g. recent zsh).

19
  • 2
    This doesn't work for me on Mac OS 10.10.3. It outputs '{375..3500..5}'.
    – aswine
    Jul 23, 2015 at 17:30
  • 6
    @aswine that's why you should always mention your OS when asking. Especially since OSX has a number of differences both from other UNICEs and from Linux.
    – terdon
    Jul 23, 2015 at 18:07
  • 2
    This isn't an OS difference per se. It's a version difference. OS X only has BASH 3 OOTB. You can install a BASH from this century using almost any OS X package manager. I use Home Brew, for one.
    – kojiro
    Jul 24, 2015 at 1:27
  • 2
    The fact that the expansions fails run directly but succeeds in bash -c pretty much guarantees that two different shells are involved. Does $SHELL --version say it's bash 3?
    – dhag
    Jul 24, 2015 at 14:00
  • 3
    @mikeserv The answer is very simply - I didn't write that because I had no idea in which version of bash this feature was introduced. I've learnt it myself from kojiro comment. And please do not compare me with SC, I really do not know all the details and history of all shells since late 1970. Once again - If you expect that, just please downvote.
    – jimmij
    Jul 25, 2015 at 19:32
20

Alternatively a traditional C-style for loop can be used:

for ((i=375; i<=3500; i+=5)); do
    echo $i
done

This is perhaps less clear than using seq, but it doesn't spawn any subprocesses. Though since I'm familiar with C, I wouldn't have any difficulty understanding this, but YMMV.

4
  • 1
    Downside of this method is that it's less portable. The seq method works in POSIX sh. Jul 23, 2015 at 15:56
  • Oh, do C-style for loops not exist in POSIX sh? You learn something new every day...
    – Muzer
    Jul 23, 2015 at 16:01
  • 2
    @WouterVerhelst Well, except for if you're limited to POSIX sh, you're not likely going to have seq, which is part of GNU coreutils. busybox has a more limited implementation, but outside of that, many embedded systems likely won't have it at all.
    – Chris Down
    Jul 23, 2015 at 22:24
  • 1
    @Muzer -- at least dash does not support it. @ChrisDown -- There's no reason why "POSIX sh" has to imply "no seq". But yeah, granted, I had overlooked the fact that seq isn't specified by POSIX. Jul 24, 2015 at 9:23
18

Using SEQ(1)

for i in $(seq 375 5 3500)
do
    echo $i
done

Or, simply:

seq 375 5 3500
1
  • 4
    Even simpler would be to drop the loop and just use seq 375 5 3500.
    – dhag
    Jul 23, 2015 at 16:51
11

Your for-loop snippet didn't work as you require for two reasons:

  • (($i += 5)) - here the $i is expanded to the value of i. Thus the expansion will be something like ((375 += 5)), which doesn't make sense (attempting to assign a literal number to another literal number). This would normally be achieved with ((i += 5)) (no $ to expand the variable)
  • The {375..3500} will be expanded before the first iteration of the loop. It will be the list of numbers 375 376 ... 3499 3500. For each iteration of the loop, i will get assigned to each of these numbers, one-by-one. Thus at the start of each iteration, i will be reassigned to the next value in that list, counting up in steps of 1. The ((i += 5)) effectively does nothing - it does add 5 to i, but then i is just reassigned again at the start of the next iteration.

I think I like the for (( ; ; )) answer best, but here are some alternatives to get you thinking:


Since we're dealing with multiples of 5, and the {a..b..i} expansion is not supported in bash version 3.2.57(1) (in OS X), then we can do this slightly arcane thing instead:

for i in 375 {38..349}{0,5} 3500; do
    echo $i
done

This demonstrates how bash can be used to create a cartesian product.


I think in general a for loop is the most convenient way to do this, but if you're interested, you can use a while-loop (a bit closer to your BASIC) program:

count=375
while (( count <= 3500 )); do
    echo $count
    (( count += 5 ))
done
7
  • 1
    @jimmij ... what's a Cartesian product? you can downvote the comment if you want, and I won't even care if you tell me. I'm curious.
    – mikeserv
    Jul 24, 2015 at 15:55
  • 1
    @mikeserv you cannot downvote the comment: en.wikipedia.org/wiki/Cartesian_product
    – jimmij
    Jul 24, 2015 at 15:56
  • @jimmij - it's still ok with me if you do.
    – mikeserv
    Jul 24, 2015 at 16:36
  • @jimmij The devil are you talking about? A Cartesian product is a set of tuples of every possible combination of elements from two sets (which may or may not be the same set). More colloquially, it's just "all the possible combinations" from two groups of things. I only see one set. How is there a Cartesian product here?
    – jpmc26
    Jul 24, 2015 at 23:02
  • 1
    @jpmc26 You are right, it is "all possible combinations", so lets plot two axis - on first write all numbers from 38 till 349. On second only two numbers: 0 and 5. Now lets create ordered pairs of those all combinations - you can write them as a sets: {38,0}, {38,5}...{349,5}, or just remove redundant symbols {, , and } and get... new numbers 380...3495.
    – jimmij
    Jul 24, 2015 at 23:31
5

While there is, of course, an app for that (seq 375 5 3500), there are various ways of doing this from the commandline. While the fastest and simplest will be just using seq, here are some other options:

for i in {375..3500}; do [[ (($i % 5)) -eq 0 ]] && echo $i; done

i=370; while [ $i -le 3500 ]; do printf "%s\n" $((i+=5)); done

perl -le '$i=shift;while($i<=$ARGV[0]){print $i; $i+=5; }' 375 3500
perl -le 'map{print $_ + 5} 370..3495'

awk 'BEGIN{ for(i=375;i<=3500;i+=5){print i}}'
1
4

POSIXly:

i=370
while [ 3500 -gt "$i" ]
do    echo "$((i+=5))"
done

...or...

echo 'for(x=370;x<=3500;x+=5)x' |bc

I dunno why you'd do it any other way. Excepting, of course...

seq 375 5 3500

...or with dc:

echo '370[5+pd3500>p]splpx'|dc
2
  • I'm curious as to how the dc code works. It's certainly more intriguing than using seq.
    – dhag
    Jul 24, 2015 at 13:56
  • 1
    @dhag - it writes a little loop. it saves the [ string ] to the top of the p array, then loads it back onto the top of stack and executes it as a macro. Within the macro we push 5 on the stack then pop it and the second from top and + them, replacing both stack values with their sum, which is printed and duplicated before 3500 is pushed on the stack, when we pop it and the dupe we just made for comparison >. If 3500 is greater, we load and execute as a macro the string stored in the p array (our current macro), or if not break.
    – mikeserv
    Jul 24, 2015 at 14:15
3

If you're stuck on Bash 3:

echo {375..3500} | tr ' ' '\n' | sed -n 'p;n;n;n;n'

and if you prefer awk:

echo {375..3500} | tr ' ' '\n' | awk '!((NR-1)%5)'

I didn't know about brace expansion -- that is seriously cool.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.