Bash while-Bedingung: Ist mein Test korrekt? Endlosschleife beinhaltet

Bash while-Bedingung: Ist mein Test korrekt? Endlosschleife beinhaltet

wie ich bereits in meinem anderen Thread erklärt habeWhile-Anweisung: Verbindung mehrerer Bedingungen funktioniert nichtIch versuche, die Eingabeargumente auf eine recursive descentbestimmte Art und Weise zu analysieren.

Traurigerweise besteht das Problem weiterhin, obwohl die Teilnehmer in diesem Thread weitaus mehr wussten als ich und gute Ratschläge gaben.

Außerdem habe ich selbst noch ein wenig in der Konsole herumgestöbert, jedoch ohne Erfolg.

Ich habe folgende zwei whileAnweisungen in meinem Skript, die problematisch sind, da die endOfInputFunktion offensichtlich keinen Einfluss auf das Beenden der While-Schleife hat.

Ich habe herumgestochert und manchmal wurde die Schleife nicht einmal angezeigt.

Das Äußere while-statementsieht also wie folgt aus:

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

Auch hier ist wieder die innerewhile-statement

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

Auch die Hilfsmethoden:

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
}

Zusätzlich gibt es noch die eat!Funktion, die uns das nächste Token aus einer Kopie des Positionsalgorithmus liefert.

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

Und darüber hinaus erkläre ich

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

current_token_index=0
curent_char_index=0

Meine Frage bezieht sich auf die Möglichkeit, dass endOfInput(ursprünglich: endOfInput?aber ich habe das gelöscht ?, da ich erfahren habe, dass es in Bash eine Platzhalterbedeutung hat, die zu Problemen führen kann. In Ruby können Sie die meisten Sonderzeichen problemlos als Bezeichner wählen. Natürlich gibt es in Bash viele Einschränkungen, wenn Sie von anderen Programmiersprachen kommen) ... sodass der Wahrheitswert der endOfInputFunktion nicht korrekt über mehrere Definitionen der Funktion zusammen mit unterschiedlicher Test- oder Vergleichssyntax in einer while-Anweisung mit ihren eigenen Anforderungen an die Auswertung, ob eine Bedingung erfüllt ist oder nicht, ausgewertet wird. Es gibt also drei Dinge in der Gleichung, die zusammen für die Formulierung der bedingten Konstellation verantwortlich sind.

Das Problem mit den whileSchleifenköpfen, wie oben beschrieben, ist, dass sie entweder nicht eingegeben werden oder nicht aufhören. Je nachdem, wie ich teste endOfInputoder wie ich es gruppiere.

Wenn ich es ! endOfInput && ...nur durch ! false && ...z.B. ersetze, wird die While-Schleife aufgerufen, im anderen Fall nicht.

Deshalb vermute ich, dass es ein Problem mit der Funktion geben muss. Sie wird nicht richtig ausgewertet. Das Problem kann 1) die Definition von sein endOfInputoder 2) wie sie getestet wird, d. h.

  • 2.1 mitwelcher Test(Dinge wie -eq, Stringvergleich wie =, arithmetische Operatoren wie ==)

  • 2.2Waswird getestet. Dh

    • 2.2.1 Eine Zeichenfolge „true“/„false“
    • 2.2.2 trueund falseals Literale
    • 2.2.3 0für trueund 1fürfalse
  • 3. Wie wird dieser Wert korrekt zurückgegeben?

    • 3.1 vonreturn
    • 3.2 vonexit
    • 3.3 vonecho
  • 4. Mit welcher Testkonstruktion wird der zurückgegebene Wert mit etwas anderem verglichen?

    • 4.1 keine, nur die Funktion ausführen
    • 4.2 Klammern ( [oder [[Befehl)
    • 4.3 Rechenklammern((...))
    • 4.4 Arithmetische Erweiterung$((...))
    • 4.5 eine oder mehrere davon kombiniert

Schauen Sie sich also bitte die Definition endOfInputund die Verwendung im Kopf der whileAnweisung an. Was könnte das Problem sein, warum gibt es eine Endlosschleife? Ich meine, die anderen beiden Funktionen isWord?funktionieren ja.

Wie muss meine Funktionsdefinition endOfInputund wie muss deren Prüfung und Verkettung in der whileAnweisung aussehen, damit sie richtig ausgewertet wird?

Edit: ilkkachu wollte, dass ich ein „minimales, vollständiges und überprüfbares Beispiel“ poste

Ich hoffe, das Folgende ist ausreichend.

Zuerst rufe ich auf, get_installed_gems_with_versionsum alle installierten Gems und ihre Versionen in einem assoziativen Array abzurufen. Jetzt habe ich das globale assoziative Array installedGems.

Dann möchte ichanalysierendie Option --gems durch Aufruf von parse_gems_with_versions, das wiederum aufruft, parseGemVersionsum eine Auswahl der installierten Gems und deren Version in$chosenGemVersions

Ich habe den Code einiger Tests weggelassen, die für den Analyseteil relevant sind, aber nicht für das aktuelle Problem der nicht funktionierenden While-Schleife.

Hier ist der Code

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
} 

Antwort1

Ich glaube, Ihr Problem liegt hauptsächlich darin, dass Sie den Booleschen Typ in Shell-Skripten missverstehen.

Die ersten Hilfsfunktionen könnt ihr grundsätzlich durch folgende ersetzen:

#!/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

Versuchen Sie nicht, „true“/„false“ oder etwas Ähnliches auszugeben. Der Testbefehl ( [..]oder [[..]]) selbst gibt den richtigen Booleschen Wert (tatsächlich eine Ganzzahl) zurück. Verwenden Sie niemals Ihre eigenen „true“/„false“-Konstrukte.

Verwenden Sie außerdem immer ShellCheck (https://www.shellcheck.net/), um Ihre Skripte kugelsicher zu machen oder sie zu debuggen.

Eine gute Angewohnheit ist auch die Verwendung von set -o nounset( set -u) und set -o errexit( set -e).


Anhand der aktualisierten Version Ihrer Frage erkenne ich, dass Sie wahrscheinlich an die C-Programmierung gewöhnt sind. In Shell-Skripten gibt es weder $ARGC, noch . Verwenden Sie stattdessen diese:$ARGV

  • ARGC(Anzahl der Argumente):"$#"

  • ARGV(Argument-Array):"$@"


Sie können beispielsweise Folgendes tun:

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

was für mich nicht viel Sinn ergibt, da es den ersten Buchstaben jedes gegebenen Arguments ausgibt. Prost und viel Glück.

Antwort2

Die Lösung besteht in der Gruppierung der über den Operator verbundenen Bedingungen ||.

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

verwandte Informationen