循环意外地在Bash脚本中无限运行

时间:2014-09-24 16:48:04

标签: bash macos shell

我在Mac上创建了一个名为Armstrong.sh的bash脚本。

这是一个检查数字是非常强的数字的函数。

# This function works properly
armstrong() {

    num=$1    # Making a copy of the received number.
    sum=0     # This will store the sum of cubes of each digit from $num



    while [ $num -gt 0];      # This loop runs while $num is greater than 0
    do
        temp=`expr $num % 10`                      # Extract the last digit of the number
        sum=`expr $sum + $temp \* $temp \* $temp`  # Cube the last digit and add it to $sum
        num=`expr $num / 10`                       # Remove the last digit of the number
    done



    if [ $sum -eq $1 ];   # If $sum == $1, i.e., If the number is armstrong
    then   
        echo "$1 is an armstrong number"        # print the number
    else
        echo "$1 is not an armstrong number"
    fi
} 

当我编写以下代码时,

armstrong 1     # this is an armstrong number
armstrong 153   # This is an armstrong number
armstrong 24    # This is not an armstrong number

然后它的输出如下,

1 is an armstrong number
153 is an armstrong number
24 is not an armstrong number
直到现在,这还不错。

问题就在这里 当我尝试使用这样的循环打印范围内的所有阿姆斯特朗数字时:

# Accept start and end point of the range
echo -n "Enter start = "
read start
echo -n "Enter end = "
read end

# Loop from start to end point and call the armstrong() function
for ((num = $start; num <= $end; num++))
do
    armstrong $num    # Calling the function.
done  

所以我的问题是:
1&gt; 如何让循环按预期运行?
2&gt; 我是否可以在$temp函数中使用armstrong()来编写代码?
就像Java中的sum += Math.pow(num%10, 3);一样 3&gt; 请给我一个更简洁的方式来编写armstrong函数。

2 个答案:

答案 0 :(得分:5)

您的函数使用变量num而不将其声明为本地变量,因此它正在更改循环引用的相同shell全局变量,从而重置循环的状态并阻止其完成。

在函数内部,更改

num=$1

local num=$1

...并且理想情况下,对函数内的所有其他变量执行相同的操作,除非您明确希望函数修改全局范围内的变量。

答案 1 :(得分:1)

该功能仅检查3位长的阿姆斯特朗号。

阿姆斯特朗数字,也称为Narcissistic Numbers,是数字,它是每个数字加到数字长度幂的总和。

正如@CharlesDuffy所指出的,为避免意外行为,函数内的变量应定义为local变量。当然,除非他们需要全球访问。

此外,当使用bash的有限整数表达式时,较大的数字会破坏任何test或计算数字。

要解决此问题,您可以使用pattern matching进行测试,使用bc进行计算:

armstrong() {

    # Initialize all local variables
    local num=$1 sum=0 num_digits=${#1}

    # Make sure number is greater than 0
    while [[ $num == [1-9]* ]]
    do
        # Raise the last digit to the length of $num and add it to $sum
        sum=$(echo "$sum + (($num % 10)^$num_digits)" | bc)

        # Remove the last digit of the number
        num=$(echo "scale=0; $num / 10" | bc)
    done

    if [[ $sum == $1 ]]
    then   
        echo "$1 is an armstrong number"
    else
        echo "$1 is not an armstrong number"
    fi

}

或者,您可以使用parameter expansion迭代num变量中的每个数字:

 for ((i=0;i<=$((num_digits-1));i++)); do
     sum=$(echo "$sum + (${num:$i:1}^$num_digits)" | bc)
 done