2014-09-24 2 views
0

Mac에서 Armstrong.sh이라는 bash 스크립트를 만들었습니다.루프가 예기치 않게 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> 어떻게 루프 원하는대로 작업을 어떻게해야합니까?
2> armstrong() 함수에 $temp을 사용하지 않고 코드를 작성할 수 있습니까?
Java에서 sum += Math.pow(num%10, 3);과 똑같습니까?
3> 더 깨끗한 방법으로 armstrong 함수를 작성하십시오.

+0

bash를 목표로하고 있기 때문에, 수학 문맥을 더 많이 사용하는 것을 고려해보십시오.'if ((sum == $ 1))'는'if [ "$ [sum"-eq "$ 1"]'보다 읽기 쉽습니다. –

+0

... 마찬가지로 현대 POSIX sh 문법은'temp = $ ((num % 10))'과'sum = $ ((sum + temp * $ temp * $ temp))'이다; POSIX Bourne을 목표로하지 않는 한 수학에'expr'을 사용할 필요가 없습니다 (요즘은 거의 알려지지 않았습니다). –

+1

쉘 스크립트 btw의 정적 분석을 위해 http://shellcheck.net/ 사용을 고려하십시오. –

답변

5

이 함수는 지역 변수를 선언하지 않고 변수 num을 사용하므로 루프가 참조하는 동일한 셸 전역 변수를 변경하므로 루프의 상태가 재설정되고 완료되지 않습니다.

기능 내부

, 사용자가 명시 적으로 원하지 않는다면 변수를 수정하는 기능을 내부에 다른 모든 변수에 대한 함수를 동일한 작업을 수행, 이상적으로,

num=$1 

local num=$1 

에 변경 ... 그리고 글로벌 범위에서.

+0

감사합니다. 그것은 효과가 있었다. :) 그런데'$ sum' 변수를'local' 변수로 사용해야합니까? –

+0

@Aditya, 네,'sum'과'temp'도 로컬로 만들어야합니다. 이것을하기위한 또 다른 방법은 함수 상단에 줄을 넣는 것입니다 :'local num sum temp' –

+0

@Aditya, ... btw, perl과 달리'$'는 쉘에서 변수 이름의 일부가 아닙니다 ; 대신 확장 연산자 (이름에 해당하는대로 해당 내용의 변수를 확장하는 데 사용됨)가 확장되지 않고 변수가 참조되는 경우에는 사용되지 않습니다. –

1

이 기능은 3 자리 길이의 암스트롱 번호 만 검사합니다.

암스트롱 번호는 Narcissistic Numbers이라고도 알려져 있으며 숫자의 길이를 기준으로 한 각 숫자의 합계 인 숫자입니다.

@CharlesDuffy는 예기치 않은 동작을 피하기 위해 함수 내의 변수를 local 변수로 정의해야합니다. 물론 전세계에 액세스해야하는 경우가 아닌 한.

또한 bash의 제한된 정수 식을 사용하면 큰 숫자로 인해 test이 깨지거나 숫자가 계산되지 않습니다.

이 문제를 해결하려면, 당신은 계산을 위해 시험 pattern matchingbc를 사용할 수 있습니다

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 변수에 각각 한 자리를 반복 수 : BTW

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