Nested Bash while loop, inner loop is only loop looping

23,055

Solution 1

You are not resetting count2. Your outer loop is running 256 times, but the inner loop stops after running through once.

If you add count2=-1 after closing inner loop, it will work as expected.

For clarity, I would move your increment so you are clearly iterating between 0-255. Here's how I'd write it:

#! /bin/sh
network_id="192.168."
count1=0
count2=0
while [ "$count1" -le 255 ]; do
    while [ "$count2" -le 255 ]; do
        printf "%s%s%s%s\n" $network_id $count1 "." $count2
        count2=$(($count2+1))
    done
    count2=0
    count1=$(($count1+1))
done

Solution 2

Nothing but a small mistake. You didnot set $count=-1

#! /bin/sh
network_id="192.168."
count1=-1
count2=-1
while [ "$count1" -le 254 ]
do
    count1=$(($count1+1))
    while [ "$count2" -le 254 ]
    do
        count2=$(($count2+1))
        printf "%s%s%s%s\n" $network_id $count1 "." $count2
    done
    count2=-1
done
exit 0

This should work.

Solution 3

Well, @uint128_t caught the error.

Your script appears to attempt to print out all the IP addresses within 192.168.0.0/16, rather than all the class C networks with that prefix, so I'll assume that's your code is a better description of the result you're looking for.

And I'll submit the following as a "better use of bash":

#!/usr/bin/env bash

# Declare an array
ip=(192 168 -1 -1)

# Step through the third quad in the array
while [ $((ip[2]++)) -lt 255 ]; do
    # Step through the fourth quad in the array
    while [ $((ip[3]++)) -lt 255 ]; do
        # Print the array, with dots as a field separator
        IFS=. eval echo '"${ip[*]}"'
    done
    # Reset the last quad once we're done with this subnet
    ip[3]=-1
done

There will be those who say that eval is evil, but it's perfectly safe in this context, where input data are known and you're protecting things with single quotes.

This solution avoids extra counters, and perhaps gives you flexibility to do other things with your IPs if you desire.

I should mention another subtlety, which is [ $((ip[2]++)) -lt 255 ]. This increments the array element, but because the ++ is AFTER the variable, the value that is used for comparison (-le) is the one before the increment occurs. So we stop the loop when the compared number was less than 255, because that means the last run of the loop will occur when the variable incremented one higher, to 255. If for some reason you wanted to compare the value after the increment instead of before, you could prepend the variable with ++ instead of appending it: $((++ip[2])).


Another fun approach might be to capitalize on the fact that IP addresses are simply numbers, and that dotted quads are a translation of that number:

#!/usr/bin/env bash

# Set your start and maximum IPs as integers
ip=$(( 192*2**24 + 168*2**16 ))
max=$(( ip + 255*2**8 + 255 ))

# Convert each integer into dotted quad notation
while [ $ip -le $max ]; do
  echo $(( ip/2**24 )).$(( ip/2**16 %256 )).$(( ip/2**8 % 256 )).$(( ip % 256 ))
  ((ip++))
done

Solution 4

For loop is better here than while loop. Your implementation can be simplified as:

#! /bin/bash
network_id="192.168."
for i in {0..255}; do
    for j in {0..255}; do
        printf "%s%s%s%s\n" $network_id $i "." $j
    done
done
exit 0

Edit: Thanks for ghoti's suggestion. Please be aware that you may need to customize the shebang line to suit your needs. More discussions can be found in:What is difference between #!/bin/sh and #!/bin/bash? and Why is it better to use “#!/usr/bin/env NAME” instead of ....

Solution 5

You should initialize count2 at the beginning of the outer loop, not before the outer loop. Otherwise, the test in the inner loop fails immdiately after the first time through the outer loop.

#! /bin/sh
network_id="192.168."
count1=-1
while [ "$count1" -le 254 ]
do
    count1=$(($count1+1))
    count2=-1
    while [ "$count2" -le 254 ]
    do
        count2=$(($count2+1))
        printf "%s%s%s%s\n" $network_id $count1 "." $count2
    done
done
exit 0
Share:
23,055
Colin93
Author by

Colin93

Updated on January 29, 2020

Comments

  • Colin93
    Colin93 about 4 years

    This is a very simple script but I can't seem to get it right. This code is supposed to print out class C IP addresses from 192.168.0.0 - 192.168.255.255 but all that prints out is 192.168.0.0 - 192.168.0.255. For some reason my outer loop won't loop. I'm sure it is something stupid but this could be helpful for those people learning nested looping in shell scripts. Any other advice is welcome!

    #! /bin/sh
    network_id="192.168."
    count1=-1
    count2=-1
    while [ "$count1" -le 254 ]
    do
        count1=$(($count1+1))
        while [ "$count2" -le 254 ]
        do
            count2=$(($count2+1))
            printf "%s%s%s%s\n" $network_id $count1 "." $count2
        done
    done
    exit 0