Bash while 조건: 내 테스트가 정확합니까? 무한 루프가 수반됩니다.

Bash while 조건: 내 테스트가 정확합니까? 무한 루프가 수반됩니다.

이미 다른 스레드에서 설명했듯이While 문: 여러 조건의 복합적인 작업을 수행할 수 없습니다.입력 인수를 어떤 방식으로든 구문 분석하려고 합니다 recursive descent.

안타깝게도 참가자들이 나보다 훨씬 더 지식이 풍부하고 좋은 조언을 제공했지만 문제는 여전히 지속됩니다.

또한 나는 아무 소용없이 콘솔에서 나 자신을 좀 더 찔렀습니다.

while내 스크립트에는 문제가 있는 다음 두 명령문이 있습니다 . 왜냐하면 endOfInput함수가 while 루프 종료에 아무런 영향을 미치지 않기 때문입니다.

나는 주위를 찌르고 때로는 루프에 들어 가지도 않을 것입니다.

따라서 외부는 while-statement다음과 같습니다.

while [ $current_token_index -le $ARGC ] && [[ "$current_token" =~ ^[a-zA-Z0-9_-]+$ ]] || ! [[ "$current_token" =~ ^[0-9]{1,2}(\.[0-9]{,2})*$ ]]; do

또, 내부는 이렇습니다while-statement

while [[ "$current_token_index" -le "$ARGC" ]] && [[ "$current_token" =~ ^[0-9]{1,2}(\.[0-9]{,2})*$ ]]; do

또한 도우미 메서드는 다음과 같습니다.

isWord? () {
    local pattern="^[a-zA-Z0-9_-]+$"
    if [[ $1 =~ $pattern ]]; then
        echo true
    else
        echo false
    fi
}

isVersionNumber? () {
    local pattern="^[0-9]{1,2}(\.[0-9]{,2})*$"
    if [[ $1 =~ $pattern ]]; then
        echo true
    else
        echo false
    fi
}

EO_ARGS=false

function endOfInput? {
 if [ $current_token_index -ge $ARGC ]; then
    EO_ARGS=true
 fi
 echo $EO_ARGS
}

또한 eat!위치 복사본에서 다음 토큰을 가져오는 기능도 있습니다.

eat! () {
        current_token=${ARGV[$current_token_index]}
        ((current_token_index += 1))
        
        current_char=${current_token:0:1}
}

그리고 그 이상으로 나는 선언합니다

# as set by eat!()
current_token=""
current_char=""

current_token_index=0
curent_char_index=0

내 질문은 endOfInput(원래: 배운 대로 endOfInput?삭제했지만 ?bash에서는 문제를 일으킬 수 있는 와일드카드 의미를 갖습니다. Ruby에서는 대부분의 특수 문자를 문제 없이 식별자로 선택할 수 있습니다. 분명히 다른 프로그래밍 언어를 사용하는 경우 bash에 많은 주의 사항이 있음) ... endOfInput평가에 대한 고유한 요구 사항이 있는 while 문의 구문을 다른 테스트 또는 비교와 함께 함수의 여러 정의를 통해 함수의 진리 값이 올바르게 평가되지 않도록 합니다. 조건이 성립하는지 아닌지. 따라서 조건부 별자리의 공식화를 담당하는 방정식에는 세 가지가 있습니다.

위에 게시된 루프 헤드 의 문제점 while은 입력되지 않거나 중지되지 않는다는 것입니다. 테스트 방법 endOfInput이나 그룹화 방법에 따라 다릅니다.

! endOfInput && ...예를 들어 교체하면 ! false && ...while 루프가 입력되고 다른 경우에는 입력되지 않습니다.

그렇기 때문에 기능에 문제가 있지 않을까 추측해봅니다. 제대로 평가되지 않았습니다. 문제는 1) 의 정의 endOfInput또는 2) 테스트 방법, 즉

  • 2.1무슨 시험( -eq, 와 같은 문자열 비교 =, 과 같은 산술 연산자 ==)

  • 2.2무엇테스트됩니다. 즉

    • 2.2.1 문자열 "true"/"false"
    • 2.2.2 truefalse리터럴
    • 2.2.3 0for true1forfalse
  • 3 이 값은 어떻게 올바르게 반환됩니까?

    • 3.1 작성자return
    • 3.2exit
    • 3.3echo
  • 4 어떤 테스트 구성으로 반환된 값을 다른 값과 비교합니까?

    • 4.1 없음, 그냥 함수 실행
    • 4.2 대괄호( [또는 [[명령)
    • 4.3 산술 괄호((...))
    • 4.4 산술 확장$((...))
    • 4.5 이들 중 하나 또는 여러 개가 결합된 것

endOfInput그러니 문장의 머리 부분에 있는 의 정의와 사용법을 살펴보시기 바랍니다 while. 무엇이 문제일 수 있으며 왜 무한 루프가 발생합니까? 내 말은, 다른 두 가지 기능이 isWord?작동한다는 것입니다.

함수 정의는 어떻게 해야 하며 endOfInput, 명령문에서 함수 정의와 테스트 및 연결은 while올바르게 평가되도록 어떻게 보여야 합니까?

편집: ilkkachu는 "최소하고 완전하며 검증 가능한 예"를 게시하기를 원했습니다.

다음 내용으로 충분하기를 바랍니다.

get_installed_gems_with_versions먼저 설치된 모든 gem과 해당 버전을 연관 배열로 가져오기 위해 호출합니다 . 이제 전역 연관 배열이 생겼습니다 installedGems.

그럼 나는하고 싶다분석하다--gems 옵션을 호출하여 설치된 gem과 해당 버전을 구문 분석하기 위해 parse_gems_with_versions호출합니다 .parseGemVersions$chosenGemVersions

구문 분석 부분과 관련이 있지만 while 루프가 작동하지 않는 경우 현재 문제와는 관련이 없는 일부 테스트의 코드를 생략했습니다.

코드는 다음과 같습니다.

get_installed_gems_with_versions () {

unset installedGems
declare -Ag installedGems

local last_key=""
local values=""

  while read -r line; do
  
    line=${line##*/}
    
    KEY="${line%-*}"

        VALUE="${line##*-}"
        
        if [ -z ${installedGems[$KEY]+x} ]; then

            echo "key $KEY doesn't yet exist."
            echo "append value $VALUE to key $KEY"
            
            installedGems[$KEY]="$VALUE"
            continue
            
        else
            echo "key already exists"
            echo "append value $VALUE to $KEY if not already exists"
            installedGems[$KEY]="${installedGems[$KEY]} $VALUE"
            echo "result: ${installedGems[$KEY]}"
        fi 
        
    done < <(find $directory -maxdepth 1 -type d -regextype posix-extended -regex "^${directory}\/[a-zA-Z0-9]+([-_]?[a-zA-Z0-9]+)*-[0-9]{1,3}(.[0-9]{1,3}){,3}\$")

 }

parseGemVersions () {
    
    local version_list
    
    declare -Ag chosenGemVersions
    
    while [[ "$current_token_index" -le "$ARGC" ]] && [[ "$current_token" =~ ^[0-9]{1,2}(\.[0-9]{,2})*$ ]]; do
    
            if versionOfGemInstalled? $gem $current_token; then
            
                if [ -z ${chosenGemVersions[$gem]+x} ]; then
                
                    chosenGemVersions[$gem]=$current_token
                # continue
                else
                    chosenGemVersions[$gem]="${chosenGemVersions[$gem]} $current_token"
                fi
                
            else
                parsing_error! "While parsing function $FUNCNAME, current version number $current_token is not installed!"
            fi
            
            echo "result: ${chosenGemVersions[$gem]}"
        
        eat!
            
    done

}

parse_gems_with_versions () {
# option --gems 
# --gems gem-name-1 1.1.1 1.2.1 1.2.5 gem-name-2 latest gem-name-3 ...

    unset gem
    unset chosenGemVersions

    gem=""
    declare -Ag chosenGemVersions

        while [ $current_token_index -le $ARGC ] && [[ "$current_token" =~ ^[a-zA-Z0-9_-]+$ ]] || ! [[ "$current_token" =~ ^[0-9]{1,2}(\.[0-9]{,2})*$ ]]; do
        
                if isWord? $current_token && [ ! "$current_token" = "latest" ]; then
                        if isWord? $current_token; then
                            if gemInstalled? $current_token; then
                                # We can conjecture that the word token is in fact a gems' name
                                gem=$current_token
                                
                                local version_list
                                
                                if isWord? $current_token && [ "$current_token" = "latest" ]; then
                                    version_list=(${installedGems[$gem]})
                                    chosenGemVersions[$gem]="${version_list[${#version_list} -1]}"
                                else
                                    # gets list chosenGemVersions
                                    parseGemVersions
                                fi
                            else
                                parsing_error! "Gem $token_name not installed!"
                            fi
    
                        fi
             else
                parsing_error! "While parsing function $FUNCNAME, "latest" is not a gemname!"
             fi
            
            eat!
            
        done
} 

답변1

귀하의 문제는 주로 쉘 스크립트의 부울 유형을 오해하는 데 있다고 생각합니다.

기본적으로 첫 번째 도우미 함수를 다음으로 바꿀 수 있습니다.

#!/bin/bash

set -o nounset
set -o errexit

is_word ()
{
    local pattern='^[a-zA-Z0-9_-]+$'
    [[ "$1" =~ $pattern ]]
}

is_version_number ()
{
    local pattern='^[0-9]{1,2}(\.[0-9]{,2})*$'
    [[ "$1" =~ $pattern ]]
}

# testing is_word ()
if is_word 'bla01'; then
    echo IS word
else
    echo NOT word
fi

# testing is_version_number ()
if is_version_number '1.1'; then
    echo IS version number
else
    echo NOT version number
fi

참/거짓 등을 에코하려고 시도하지 마세요. 테스트 명령( [..]또는 [[..]]) 자체가 적절한 부울(실제로는 정수)을 반환하므로 자신만의 참/거짓 구성을 사용하지 마세요.

또한 항상 ShellCheck(https://www.shellcheck.net/) 스크립트를 방탄하거나 디버깅합니다.

set -o nounset( set -u)와 set -o errexit( )를 사용하는 것도 좋은 습관이다 set -e.


질문의 업데이트된 버전을 보면 아마도 C 프로그래밍에 익숙하신 것으로 보입니다. 쉘 스크립트에는 $ARGC, 도 없습니다 . $ARGV대신 다음을 사용하세요.

  • ARGC(인수 개수):"$#"

  • ARGV(인수 배열):"$@"


예를 들어 다음과 같이 할 수 있습니다.

eat_args ()
{
    for arg in "$@"; do
        current_char=${arg:0:1}
        echo "$current_char"
    done
}
eat_args "$@"

이것은 주어진 각 인수의 첫 글자를 인쇄하기 때문에 나에게는 별로 의미가 없습니다. 건배와 행운을 빕니다.

답변2

솔루션은 연산자를 통해 연결된 조건을 그룹화하는 것입니다 ||.

while [[ $current_token_index -le $ARGC ]] && { [[ "$current_token" =~ ^[a-zA-Z0-9_-]+$ ]] || ! [[ "$current_token" =~ ^[0-9]{1,2}(\.[0-9]{,2})*$ ]]; }; do 

관련 정보