While-Anweisung: Verbindung mehrerer Bedingungen funktioniert nicht

While-Anweisung: Verbindung mehrerer Bedingungen funktioniert nicht

Ich versuche, Argumente anhand eines recursive descentSchemas zu analysieren.

Die whileAnweisungen rufen eat!nach jeder Ausführung eine Funktion auf, um den nächsten $current_tokenProzess abzurufen.

Das Problem ist nun, dass es dadurch in eine Endlosschleife gerät.

Um genau zu sein, gibt es in der Funktion zwei While-Schleifen.

Entweder einer von beiden oder beide funktionieren nicht, weil die Verbindung syntaktisch (oder logisch?) falsch ist.

Könnten Sie bitte auf den vielen möglichen Ebenen überprüfen, was mit dem folgenden Code nicht in Ordnung sein könnte?

1)

while $(isWord? $current_token) || ! $(isVersionNumber? $current_token) && ! $(endOfInput?); do
while isVersionNumber? $current_token && ! endOfInput?; do

Und was ist richtig? Sollen die Prüffunktionen in eine Befehlssubstitution eingefügt werden oder nicht?

Könnte es sein, dass ein Kurzschluss auch den endOfInput?-Test verhindert?

Denn das sollte die Schleife unbedingt stoppen.

Auf Anfrage von ilkkachu

Der Code der Testfunktionen:

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=1

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

Ich gehe davon aus, dass es einen anderen Befehl ausgibt, da Sie es in einer Befehlsersetzung verwenden?

Nein, es war nur ein Versuch, weil ich nicht weiß, ob ich die Funktionen im Bedingungssatz ohne Befehlsersetzung aufrufen kann.

Der Vollständigkeit halber füge ich die eat!Funktion auch hinzu.

eat! () {
    
if [[ ! $(($current_char_index + 1)) -gt $ARGC ]]; then
  current_token=${ARGV[$current_token_index]}
  ((current_token_index += 1))

  current_char=${current_token:0:1}
}

testUnd könnten die Bedingungen vielleicht besser mit / [( ) formuliert werden [[?

Könnte das Ausrufezeichen die Fehlerstelle sein?

Folgendes scheint syntaktisch falsch zu sein:

while [ endOfInput -eq 1 ] && isWord? "$current_token" || ! isVersionNumber? "$current_token"; do

Aus der Klammer [erhalte ich die Fehlermeldung

[: endOfInput: integral expression expected (translation)

Antwort1

Wenn eine Funktion 0 für „true“ und etwas anderes für „false“ zurückgibt, können Sie sie direkt verwenden:

my-true () {
    return 0
}

while my-true ; do
    echo Infinite loop...
done

Wenn bei false nichts ausgegeben wird, bei true aber etwas, verwenden Sie die Befehlssubstitution

my-true () {
    echo OK
}
while [ "$(my-true)" ] ; do
    echo Infinite loop...
done

Um die Funktionen im ersten Fall zu verketten, können Sie die Operatoren &&„und“ verwenden ||:

while func1 && func2 ; do

Im letzteren Fall können Sie anstelle von Folgendem eine Verkettung verwenden ||:

while [ "$(func1 ; func2)" ] && [ "$(func3)" ]

Beachten Sie, dass Sie im letzteren Fall keine globalen Variablen in der Funktion ändern können, da diese in einer Subshell aufgerufen wird.

Übrigens ?ist die Verwendung in einem Funktionsnamen nicht verboten, kann aber Probleme verursachen, da es sich um ein Platzhalterzeichen handelt:

abc? () {
    return $1
}
abc? 0   # Everything OK.
touch abcX
abc? 0   # line 9: ./abcX: Permission denied
abc\? 0  # This works, but where's the charm?

!ist das Standardzeichen zur Verlaufserweiterung, das aber hauptsächlich nur für interaktive Shells relevant ist.

Antwort2

Könnten Sie bitte auf den vielen möglichen Ebenen überprüfen, was mit dem folgenden Code nicht in Ordnung sein könnte?

Ok, gut.

Und was ist richtig? Sollen die Prüffunktionen in eine Befehlssubstitution eingefügt werden oder nicht?

Eine Befehlsersetzung nimmt den Befehl im Inneren andrucktund setzt das in die Kommandozeile. (Nach dem Splitten und Globbing, wenn die Erweiterung nicht in Anführungszeichen gesetzt war.) Wenn Sie also

foo() {
    echo true
}
if $(foo); then
    echo hello
fi

dann würde die Befehlssubstitution Folgendes erzeugen true, das im Bedingungsteil der if-Anweisung ausgeführt würde, und der Beendigungsstatus davon würde verwendet werden, um zu bestimmen, ob der Hauptzweig ifausgeführt wird. In diesem Fall wäre dies der Fall.

Aber das ist albern. Sie könnten den Beendigungsstatus direkt zurückgeben, ohne eine Befehlsersetzung durchzuführen (was in Bash eine Verzweigung beinhaltet):

foo() {
    return 0
}
if foo; then
    echo hello
fi

Oder Sie beenden die Funktion einfach mit true, da der Beendigungsstatus des letzten Befehls als Beendigungsstatus der Funktion verwendet wird.

Sie könnten also schreiben:

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

oder auch:

isWord? () {
    local pattern="^[a-zA-Z0-9_-]+$"
    [[ $1 =~ $pattern ]]
}
while isWord? "$current_token"

Beachten Sie, dass Sie die Variable aus den üblichen Gründen (z. B. Worttrennung und Globbing) in Anführungszeichen setzen müssen.

while $(isWord? $current_token) || ! $(isVersionNumber? $current_token) && ! $(endOfInput?); do

Beachten Sie hier, dass die a || b && cKonstruktion als gruppiert (a || b) && c, d. h. sie wird von links nach rechts ausgeführt. Anders als in echten Programmiersprachen, wo einUndOperator hat eine höhere Priorität alsoder(was bedeutet, dass es stattdessen wäre a || (b && c)). Prüfen Sie also, welches Sie benötigen.

Auch wenn mit den Standardeinstellungen von Bash ein Name wie isWord?vielleicht nicht allzu oft ein Problem darstellt, handelt es sich dennoch um einen Glob. Wenn Sie eine oder mehrere Dateien haben, die mit diesem Namen im aktuellen Verzeichnis übereinstimmen, wird er zu diesen Dateinamen erweitert. Und wenn Sie auf die Verwendung von failgloboder umstellen nullglob, wirkt sich dies ebenfalls darauf aus. Zum Beispiel mit failglob:

$ foo? () { echo hi; }
$ foo?
bash: no match: foo?
$ "foo?"
hi

Sie müssen es in Anführungszeichen setzen, damit es sich nicht wie ein Glob verhält. Beachten Sie, dass in Zsh ein nicht übereinstimmender Glob standardmäßig fehlschlägt.

Das Ausrufezeichen als letztes Zeichen eines Funktionsnamens sollte keine Rolle spielen. Es könnte die Verlaufserweiterung treffen, wenn es am Anfang oder in der Mitte des Namens steht und die Shell die Verlaufserweiterung aktiviert hat (interaktive Shells tun dies standardmäßig, Skripte nicht).

Könnte es sein, dass durch das Kurzschließen auch der endOfInput?-Test verhindert wird?

Wenn Sie so etwas haben wie while bar && ! endOfInput?; do, dann ja, wenn barauf der linken Seite ein falscher Status zurückgegeben wird, das Ergebnis derUndist bereits bekannt und die rechte Seite wird übersprungen. Gleiches gilt für ||und eine wahrheitsgetreue Rückgabe. Das ist, was &&und ||tun.

Sie haben zu dieser Frage jedoch keinen Kontext angegeben, d. h. warum es für Sie wichtig sein sollte, wenn die Funktion nicht aufgerufen wird. Daher fällt es Ihnen schwer, sich bessere Möglichkeiten vorzustellen, dies zu tun.

verwandte Informationen