Я пытаюсь разобрать аргументы по recursive descent
схеме.
Операторы while
вызывают eat!
функцию после каждого выполнения, чтобы начать следующую $current_token
обработку.
Проблема в том, что таким образом мы попадаем в бесконечный цикл.
Если быть точным, в функции есть два цикла while.
Либо один из них, либо оба не работают, поскольку соединение синтаксически (или логически?) неверно.
Не могли бы вы проверить, что может быть не так в следующем коде на всех возможных уровнях?
1)
while $(isWord? $current_token) || ! $(isVersionNumber? $current_token) && ! $(endOfInput?); do
while isVersionNumber? $current_token && ! endOfInput?; do
И как правильно? Помещать проверочные функции в подстановку команды или нет?
Может ли быть, что короткое замыкание также препятствует endOfInput?
проведению теста?
Потому что это должно полностью положить конец этому циклу.
По просьбе ilkkachu
Код тестовых функций:
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
}
Я предполагаю, что он выводит другую команду, поскольку вы используете ее в подстановке команд?
Нет, это была просто попытка, потому что я не знаю, смогу ли я просто вызвать функции в условном операторе без подстановки команд.
Для полноты картины я также добавляю eat!
функцию.
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
/ [
( [[
) ?
Может ли восклицательный знак быть точкой провала?
Следующий текст кажется синтаксически неверным:
while [ endOfInput -eq 1 ] && isWord? "$current_token" || ! isVersionNumber? "$current_token"; do
Из скобок [
я получаю сообщение об ошибке
[: endOfInput: integral expression expected (translation)
решение1
Если функция возвращает 0 для значения true и что-либо другое для значения false, вы можете использовать ее напрямую:
my-true () {
return 0
}
while my-true ; do
echo Infinite loop...
done
Если при значении false ничего не выводится, но при значении true выводится что-то, используйте подстановку команд.
my-true () {
echo OK
}
while [ "$(my-true)" ] ; do
echo Infinite loop...
done
Для объединения функций в цепочку в первом случае можно использовать операторы &&
и ||
:
while func1 && func2 ; do
В последнем случае вы можете использовать конкатенацию вместо ||
:
while [ "$(func1 ; func2)" ] && [ "$(func3)" ]
Обратите внимание, что в последнем случае вы не можете изменять глобальные переменные в функции, так как она вызывается в подоболочке.
Кстати, использование ?
в имени функции не запрещено, но может вызвать проблемы, так как это подстановочный знак:
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?
!
— это символ расширения истории по умолчанию, но он в основном актуален только для интерактивных оболочек.
решение2
Не могли бы вы проверить, что может быть не так в следующем коде на всех возможных уровнях?
Хорошо.
И как правильно? Помещать проверочные функции в подстановку команды или нет?
Подстановка команды берет то, что находится внутри командыотпечатки, и помещает это в командную строку. (После разделения и подстановки, если расширение не было заключено в кавычки.) Так что если у вас есть
foo() {
echo true
}
if $(foo); then
echo hello
fi
то подстановка команды произведет true
и это будет выполнено в условной части -statement if
, и статус выхода из него будет использован для определения того, выполняется ли основная ветвь if
. В этом случае это будет сделано.
Но это глупо. Вы могли бы просто вернуть статус выхода напрямую, без подстановки команд (которая в Bash подразумевает форк):
foo() {
return 0
}
if foo; then
echo hello
fi
Или вы можете просто завершить функцию с помощью true
, поскольку статус выхода последней команды используется как статус выхода функции.
Итак, вы можете написать:
isWord? () {
local pattern="^[a-zA-Z0-9_-]+$"
if [[ $1 =~ $pattern ]]; then
return 0
else
return 1
fi
}
while isWord? "$current_token"
или даже:
isWord? () {
local pattern="^[a-zA-Z0-9_-]+$"
[[ $1 =~ $pattern ]]
}
while isWord? "$current_token"
Обратите внимание, что вам необходимо заключить переменную в кавычки по всем обычным причинам (например, для разделения слов и подстановки).
while $(isWord? $current_token) || ! $(isVersionNumber? $current_token) && ! $(endOfInput?); do
Здесь обратите внимание, что a || b && c
конструкция группируется как (a || b) && c
, т.е. она выполняется слева направо. В отличие от реальных языков программирования, гдеиоператор имеет более высокий приоритет, чемили(то есть вместо этого будет a || (b && c)
). Так что проверьте, какой из них вам нужен.
Кроме того, хотя при настройках по умолчанию в Bash имя типа , isWord?
возможно, не слишком часто является проблемой, оно все равно является glob. Если у вас есть файл или файлы, соответствующие ему в текущем каталоге, он будет расширен до этих имен файлов. И если вы переключитесь на использование failglob
или nullglob
, они повлияют и на это. Например, с failglob
:
$ foo? () { echo hi; }
$ foo?
bash: no match: foo?
$ "foo?"
hi
Вам нужно будет заключить его в кавычки, чтобы он не вел себя как глоб. Обратите внимание, что в Zsh несоответствующий глоб по умолчанию терпит неудачу.
Восклицательный знак в качестве последнего символа имени функции не должен иметь значения. Он может вызвать расширение истории, если он находится в начале или середине имени, а в оболочке включено расширение истории (интерактивные оболочки по умолчанию это делают, скрипты — нет).
Может ли быть, что короткое замыкание также препятствует тесту endOfInput?
Если у вас есть что-то вроде while bar && ! endOfInput?; do
, то да, если bar
в левой части возвращается ложный статус, результатиуже известно, а правая часть пропускается. Аналогично для ||
и правдивого ретурха. Вот что &&
и ||
делают.
Но вы не дали никакого контекста этому вопросу, т. е. почему для вас имеет значение, если функция не вызывается, поэтому сложно придумать лучшие способы сделать это.