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 Строка «истина»/«ложь»
    • 2.2.2 trueи falseкак литералы
    • 2.2.3 0для trueи 1дляfalse
  • 3 Как правильно возвращается это значение?

    • 3.1 поreturn
    • 3.2 поexit
    • 3.3 поecho
  • 4 с помощью какой тестовой конструкции сравнивается возвращаемое значение с чем-то еще?

    • 4.1 нет, просто выполнение функции
    • 4.2 скобки ( [или [[команда)
    • 4.3 арифметические скобки((...))
    • 4.4 арифметическое расширение$((...))
    • 4.5 один или несколько из них в сочетании

Поэтому, пожалуйста, взгляните на определение endOfInputи как оно используется в заголовке утверждения while. В чем может быть проблема, почему есть бесконечный цикл? Я имею в виду, что другие две функции вроде isWord?работают.

Как должно выглядеть определение моей функции, endOfInputа также ее тестирование и объединение в операторе, чтобы она была правильно оценена?while

Редактировать: ilkkachu хотел, чтобы я опубликовал «минимальный, полный и проверяемый пример»

Надеюсь, нижеизложенного будет достаточно.

Сначала я вызываю get_installed_gems_with_versions, чтобы получить все установленные гемы и их версии в ассоциативном массиве. Теперь у меня есть глобальный ассоциативный массив installedGems.

Тогда я хочуразобратьопция --gems вызывается parse_gems_with_versions, которая в свою очередь вызывается parseGemVersionsдля анализа набора установленных gems и их версии в$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

Видите ли, не пытайтесь вывести true/false или что-то в этом роде, тестовая команда ( [..]или [[..]]) сама возвращает правильное логическое значение (на самом деле целое число), никогда не используйте собственные конструкции true/false.

Кроме того, всегда используйте 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 

Связанный контент