Ich versuche, Argumente anhand eines recursive descent
Schemas zu analysieren.
Die while
Anweisungen rufen eat!
nach jeder Ausführung eine Funktion auf, um den nächsten $current_token
Prozess 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}
}
test
Und 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 if
ausgefü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 && c
Konstruktion 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 failglob
oder 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 bar
auf 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.