wie ich bereits in meinem anderen Thread erklärt habeWhile-Anweisung: Verbindung mehrerer Bedingungen funktioniert nichtIch versuche, die Eingabeargumente auf eine recursive descent
bestimmte 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 while
Anweisungen in meinem Skript, die problematisch sind, da die endOfInput
Funktion 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-statement
sieht 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 endOfInput
Funktion 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 while
Schleifenköpfen, wie oben beschrieben, ist, dass sie entweder nicht eingegeben werden oder nicht aufhören. Je nachdem, wie ich teste endOfInput
oder 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 endOfInput
oder 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
true
undfalse
als Literale - 2.2.3
0
fürtrue
und1
fürfalse
3. Wie wird dieser Wert korrekt zurückgegeben?
- 3.1 von
return
- 3.2 von
exit
- 3.3 von
echo
- 3.1 von
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 endOfInput
und die Verwendung im Kopf der while
Anweisung 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 endOfInput
und wie muss deren Prüfung und Verkettung in der while
Anweisung 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_versions
um 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, parseGemVersions
um 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