как я уже объяснил в другой темеОператор While: Невозможно заставить работать соединение нескольких условийЯ пытаюсь проанализировать входные аргументы определенным образом recursive descent
.
К сожалению, проблема все еще сохраняется, хотя участники обсуждения были гораздо более осведомлены, чем я, и давали хорошие советы.
Я также покопался в консоли, но безрезультатно.
В моем скрипте есть два следующих while
оператора, которые являются проблемными, поскольку, очевидно, endOfInput
функция не влияет на завершение цикла while.
Я копался в этом, но иногда это даже не входило в цикл.
Итак, внешний вид while-statement
следующий:
while [ $current_token_index -le $ARGC ] && [[ "$current_token" =~ ^[a-zA-Z0-9_-]+$ ]] || ! [[ "$current_token" =~ ^[0-9]{1,2}(\.[0-9]{,2})*$ ]]; do
И, опять же, вот внутренняяwhile-statement
while [[ "$current_token_index" -le "$ARGC" ]] && [[ "$current_token" =~ ^[0-9]{1,2}(\.[0-9]{,2})*$ ]]; do
Также вспомогательные методы:
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
}
Кроме того, eat!
функция, которая возвращает нам следующий токен из копии позиционного
eat! () {
current_token=${ARGV[$current_token_index]}
((current_token_index += 1))
current_char=${current_token:0:1}
}
И сверх того я заявляю,
# as set by eat!()
current_token=""
current_char=""
current_token_index=0
curent_char_index=0
Мой вопрос касается возможности того, что endOfInput
(первоначально: endOfInput?
но я удалил ?
, как я узнал, в bash он имеет подстановочное значение, что может привести к проблемам. В Ruby вы можете выбрать большинство специальных символов в качестве идентификаторов без проблем. Очевидно, что в bash есть много оговорок, если вы пришли из других языков программирования) ... так что истинностное значение функции endOfInput
не оценивается правильно с помощью нескольких определений функции вместе с различным тестированием или сравнением синтаксиса в операторе while с его собственными требованиями к оценке того, выполняется ли условие или нет. Таким образом, в уравнении есть три вещи, которые вместе отвечают за формулировку условного созвездия.
Проблема с while
заголовками цикла, как указано выше, заключается в том, что они либо не вводятся, либо не останавливаются. В зависимости от того, как я тестирую endOfInput
или как я это группирую.
Если я заменю ! endOfInput && ...
просто на , ! false && ...
например, цикл while будет запущен, в противном случае — нет.
Вот почему я предполагаю, что должна быть проблема с функцией. Она не правильно оценена. Проблема может быть в 1) определении endOfInput
, или 2) в том, как она проверяется, то есть,
2.1 скакой тест(такие вещи
-eq
, как сравнение строк=
, такие арифметические операторы==
)2.2чтопроверено. Т.е.
- 2.2.1 Строка «истина»/«ложь»
- 2.2.2
true
иfalse
как литералы - 2.2.3
0
дляtrue
и1
дляfalse
3 Как правильно возвращается это значение?
- 3.1 по
return
- 3.2 по
exit
- 3.3 по
echo
- 3.1 по
4 с помощью какой тестовой конструкции сравнивается возвращаемое значение с чем-то еще?
- 4.1 нет, просто выполнение функции
- 4.2 скобки (
[
или[[
команда) - 4.3 арифметические скобки
((...))
- 4.4 арифметическое расширение
$((...))
- 4.5 один или несколько из них в сочетании
Поэтому, пожалуйста, взгляните на определение endOfInput
и как оно используется в заголовке утверждения while
. В чем может быть проблема, почему есть бесконечный цикл? Я имею в виду, что другие две функции вроде isWord?
работают.
Как должно выглядеть определение моей функции, endOfInput
а также ее тестирование и объединение в операторе, чтобы она была правильно оценена?while
Редактировать: ilkkachu хотел, чтобы я опубликовал «минимальный, полный и проверяемый пример»
Надеюсь, нижеизложенного будет достаточно.
Сначала я вызываю get_installed_gems_with_versions
, чтобы получить все установленные гемы и их версии в ассоциативном массиве. Теперь у меня есть глобальный ассоциативный массив installedGems
.
Тогда я хочуразобратьопция --gems вызывается parse_gems_with_versions
, которая в свою очередь вызывается parseGemVersions
для анализа набора установленных gems и их версии в$chosenGemVersions
Я оставлю код некоторых тестов, которые имеют отношение к части синтаксического анализа, но не к текущей проблеме, если не работает цикл while.
Итак, вот код
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
}
решение1
Я полагаю, что ваша проблема в основном в непонимании логического типа в скриптах оболочки.
По сути, вы можете заменить первые вспомогательные функции следующими:
#!/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
Видите ли, не пытайтесь вывести true/false или что-то в этом роде, тестовая команда ( [..]
или [[..]]
) сама возвращает правильное логическое значение (на самом деле целое число), никогда не используйте собственные конструкции true/false.
Кроме того, всегда используйте ShellCheck (https://www.shellcheck.net/) для защиты ваших скриптов или их отладки.
Хорошей привычкой также является использование set -o nounset
( set -u
) и set -o errexit
( set -e
).
С обновленной версией вашего вопроса я вижу, что вы, вероятно, привыкли к программированию на C. В скриптах оболочки нет $ARGC
, и . Вместо этого используйте это:$ARGV
ARGC
(количество аргументов):"$#"
ARGV
(массив аргументов):"$@"
Например, вы можете сделать так:
eat_args ()
{
for arg in "$@"; do
current_char=${arg:0:1}
echo "$current_char"
done
}
eat_args "$@"
что не имеет для меня особого смысла, так как печатает первую букву каждого данного аргумента. Удачи и приветствия.
решение2
Решением является группировка условий, связанных через ||
оператора.
while [[ $current_token_index -le $ARGC ]] && { [[ "$current_token" =~ ^[a-zA-Z0-9_-]+$ ]] || ! [[ "$current_token" =~ ^[0-9]{1,2}(\.[0-9]{,2})*$ ]]; }; do