as I have already explained in my other thread While statement: Cannot get compound of multiple conditions to work I am trying to parse the input arguments in a recursive descent
fashion.
Sadly, the thread, although the participants were by far more knowledgable than me and gave good advice, the problem still persists.
Also I poked around myself some further in the console with no avail.
I have following two while
statements in my script, which are problematic, because obviously the endOfInput
function has no influence on ending the while loop.
I would poke around and sometimes it wouldn't even enter the loop.
So the outer while-statement
is the following:
while [ $current_token_index -le $ARGC ] && [[ "$current_token" =~ ^[a-zA-Z0-9_-]+$ ]] || ! [[ "$current_token" =~ ^[0-9]{1,2}(\.[0-9]{,2})*$ ]]; do
Also, again, here is the inner while-statement
while [[ "$current_token_index" -le "$ARGC" ]] && [[ "$current_token" =~ ^[0-9]{1,2}(\.[0-9]{,2})*$ ]]; do
Also the helper methods:
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
}
Additionally the eat!
function that gets us the next token from a copy of the positional
eat! () {
current_token=${ARGV[$current_token_index]}
((current_token_index += 1))
current_char=${current_token:0:1}
}
And above that I declare
# as set by eat!()
current_token=""
current_char=""
current_token_index=0
curent_char_index=0
My question refers to the possibility, that the endOfInput
(originally: endOfInput?
but I deleted the ?
as I learned, in bash it has a wildcard meaning which can lead to problems. In Ruby you can choose most special characters as identifiers with no problems. Obviously there are many caveats in bash if you come from other programming languages) ... so that the truth value of endOfInput
function is not evaluated correctly via multiple definitions of the function together with different testing or comparing syntax in a while-statement with its own exigences towards evaluating whether or not a condition holds or not. So there are three things in the equation that together are responsible for the formulation of the conditional constellation.
The problem with the while
loop heads as posted above are that either they aren't entered or they don't stop. Depending on how I test endOfInput
or how I group it.
If I replace ! endOfInput && ...
just by ! false && ...
for instance the while loop would be entered, in the other case not.
This is why I conjecture that there must be a problem with the function. It is not correctly evaluated. The problem may be 1) the definition of endOfInput
, or 2) how it gets tested that is,
2.1 with what test (things like
-eq
, string comparison like=
, arithmetic operators like==
)2.2 what is tested. I.e.
- 2.2.1 A string "true"/"false"
- 2.2.2
true
andfalse
as literals - 2.2.3
0
fortrue
and1
forfalse
3 How is this value correctly returned?
- 3.1 by
return
- 3.2 by
exit
- 3.3 by
echo
- 3.1 by
4 by what testing construct is the returned value compared to something else?
- 4.1 none, just executing the function
- 4.2 brackets (
[
or[[
command) - 4.3 arithmetic parentheses
((...))
- 4.4 arithmetic expansion
$((...))
- 4.5 one or multiple of those combined
So please take a look at the definition of endOfInput
and how it is used in the head of the while
statement. What could be the problem, why there is an infinity loop? I mean, the other two functions like isWord?
do work.
How should my function definition of endOfInput
and how should the testing and concatenation of it in the while
statement should look like such that it is correctly evaluated?
Edit: ilkkachu wanted me to post a "minimal, complete and verifiable example"
I hope the following is sufficient.
First I call get_installed_gems_with_versions
to get all installed gems and their versions in an associative array. Now I have the global associative array installedGems
.
Then I want to parse the option --gems by calling parse_gems_with_versions
, which in turn calls parseGemVersions
in order to parse a selection of the installed gems and their version into $chosenGemVersions
I let away the code of some tests that are relevant to the parsing part but not to the current problem if the not working while loop.
So here is the 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
}
endOfInput
, the code you showed only has a function calledendOfInput?
. Which ever it is, it wouldn't affect the conditions in those twowhile
loops, because neither of them seems to a) call the function, nor b) use the variableEO_ARGS
set by the function. – ilkkachu Jun 05 '21 at 12:29endOfinput
not doing what you expect it to do, so you might want to look there. (Well, minus the fact that your code here doesn't contain a line with! endOfInput &&
...) – ilkkachu Jun 05 '21 at 12:40endOfInput
And I did convey what it is what I want to do. I am parsing the arguments of a script "in the fashion of" a https://en.wikipedia.org/wiki/Recursive_descent_parser
– von spotz Jun 05 '21 at 12:46eat!
function. Now, that one we can look at, since you did include it separately, and we can see it setscurrent_token
, which is a major which shellcheck.net notes that is used but not set in your code. But theneat!
uses an array calledARGV
, and nowhere is it shown what that is. – ilkkachu Jun 06 '21 at 20:55