嵌套 Bash while 循环,内循环只是循环循环

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/35192252/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 14:11:59  来源:igfitidea点击:

Nested Bash while loop, inner loop is only loop looping

bashshellloopswhile-loopnested-loops

提问by Colin93

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!

这是一个非常简单的脚本,但我似乎无法正确理解。这段代码应该打印出 192.168.0.0 - 192.168.255.255 的 C 类 IP 地址,但打印出来的只是 192.168.0.0 - 192.168.0.255。出于某种原因,我的外循环不会循环。我确信这很愚蠢,但这可能对那些在 shell 脚本中学习嵌套循环的人有所帮助。欢迎任何其他建议!

#! /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

回答by uint128_t

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

你不是在重置count2。您的外循环运行了 256 次,但内循环在运行一次后停止。

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

如果count2=-1在关闭内循环后添加,它将按预期工作。

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

为清楚起见,我会移动您的增量,以便您清楚地在 0-255 之间进行迭代。这是我如何写的:

#! /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

回答by sudheesh shetty

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

只是一个小错误。你没有设置$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.

这应该有效。

回答by ghoti

Well, @uint128_tcaught the error.

好吧,@uint128_t发现了错误。

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.

您的脚本似乎试图打印出 192.168.0.0/16 内的所有 IP 地址,而不是带有该前缀的所有 C 类网络,因此我假设您的代码更好地描述了您正在寻找的结果.

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

我将提交以下内容作为“更好地使用 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 evalis evil, but it's perfectly safe in this context, where input data are known and you're protecting things with single quotes.

会有人说这eval是邪恶的,但在这种情况下它是完全安全的,输入数据是已知的,并且你用单引号保护事物。

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

此解决方案避免了额外的计数器,并且如果您愿意,也许可以让您灵活地使用您的 IP 执行其他操作。

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])).

我应该提到另一个微妙之处,那就是[ $((ip[2]++)) -lt 255 ]. 这会增加数组元素,但因为++是在变量之后,所以用于比较 ( -le)的值是发生增加之前的值。所以当比较的数字小于 255 时我们停止循环,因为这意味着当变量增加 1 到 255 时将发生最后一次循环。如果由于某种原因你想比较增量后的值而不是之前,您可以在变量前面加上++而不是附加它:$((++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:

另一个有趣的方法可能是利用 IP 地址只是数字这一事实,而虚线四边形是该数字的翻译:

#!/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

回答by Walter A

You can use 1 loop:

您可以使用 1 个循环:

i=0
((max=256 * 256))
while [ $i -lt ${max} ]; do
        (( major=i/256))
        (( minor=i%256))
        printf "%s.%s.%s.%s\n" "192" "168"  ${major} ${minor}
        ((++i))
done

回答by Quinn

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

For 循环在这里比 while 循环更好。您的实现可以简化为:

#! /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 ....

编辑:感谢 ghti 的建议。请注意,您可能需要定制shebang系列以满足您的需求。更多讨论可以在:#!/bin/sh 和 #!/bin/bash 之间有什么区别?以及为什么最好使用“#!/usr/bin/env NAME”而不是 ...

回答by Barmar

You should initialize count2at 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.

您应该count2在外循环开始时初始化,而不是在外循环之前。否则,在第一次通过外循环后,内循环中的测试会立即失败。

#! /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