嵌套的Bash while循环,内部循环仅循环一次

4

这是一个非常简单的脚本,但我似乎无法正确运行它。此代码应该从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

2
请注意,在双括号结构中不需要使用“$”。例如,"$((count1 + 1))"可以完美地工作。 - vastlysuperiorman
1
请注意,当您使用 #!/bin/sh 在 POSIX 兼容模式下运行脚本时,这意味着它们将是可移植的。下面的答案建议使用 {0..255}。虽然 BASH 可以扩展它,但其他 POSIX /bin/sh 实现可能不会。如果您正在为 bash 编写代码,则最好使用 #!/usr/bin/env bash 作为您的 shebang。如果您要编写可移植性代码,则最好不要使用 bashisms。 - ghoti
有趣的是,前两个while循环的答案在内部while完成后都重置了count2。我会在它开始之前设置它(而不是在外层循环开始之前)。但两种技巧都可以实现相同的结果。 - Jonathan Leffler
6个回答

2

您没有重置count2。您的外循环运行了256次,但内循环只运行一次后就停止了。

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

为了清晰起见,我建议将增量移动,以便您明确地在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

1
哇,我就知道是这样的。这只是编程的事情,甚至不是一个shell脚本的事情。我感觉很蠢,但还是谢谢你! - Colin93
1
@Colin93 如果这个回答解决了你的问题,请记得选择它作为正确答案。 :) - vastlysuperiorman

2

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

这应该可以工作。


2

@uint128_t捕获了错误。

你的脚本似乎试图打印出192.168.0.0/16内的所有IP地址,而不是该前缀下的所有C类网络,所以我假设你的代码更好地描述了你要查找的结果。

我将提交以下内容作为“更好的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

有些人会说eval是邪恶的,但在这个上下文中,它非常安全,因为输入数据已知,并且你正在用单引号保护它们。
这个解决方案避免了额外的计数器,也许给你灵活性去做其他事情,如果你想的话。
我应该提到另一个微妙之处,即[ $((ip[2]++)) -lt 255 ]。这增加了数组元素,但因为 ++ 在变量之后,用于比较(-le)的值是增量发生之前的值。因此,当比较的数字小于255时,我们停止循环,因为这意味着循环的最后一次运行将在变量增加一个更高的值时发生,即255。如果出于某种原因,你希望比较增量之后的值而不是之前的值,可以在变量前面添加 ++ 而不是附加它:$((++ip[2]))
另一个有趣的方法可能是利用 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

1

1
根据我的评论,如果你指定了 #!/bin/sh 的 shebang,请不要使用 {0..255}。最明显的是,并非所有的 /bin/sh 都是 bash。 (我有一个 Ubuntu 系统,其中 /bin/shdash,而不是 bash。)如果你想实现可移植性和 POSIX 兼容性,请勿使用 bashism。 - ghoti
1
替换 shebang 不是一个理想的解决方案。用一些 POSIX 可移植的东西来替换循环结构可能是一个更好的解决方案。 - tripleee
@ccf,我提到 #!/usr/bin/env bash 的原因是因为问题涉及到 bash,没有指定操作系统。在FreeBSD中,bash通常位于 /usr/local/bin/bash。在OSX中,系统自带bash版本3,如果你从macports安装了bash 4,则可能在 /opt/local/bin/bash 中找到它。如果您无法确定bash二进制文件的位置,则可以信任 /usr/bin/env 来为您找到它。可移植性++. - ghoti
@ghoti:尽管如此,问题指定了“#! /bin/sh”。所以我只是假设这对这个问题有效。谢谢你指出来。 - Quinn
对于发布问题的人来说,这可能已经足够了,但为了在StackOverflow上建立有用的问答集合,最好提供可以在其他环境中使用的答案。一年后搜索该网站的某个人可能会尝试在FreeBSD中执行相同的操作,并且可能不知道bash的不同位置。正如我所说,可移植性++。 - ghoti
显示剩余2条评论

1

你应该在外部循环的开始处初始化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

1
你可以使用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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接