Оператор While: Невозможно заставить работать соединение нескольких условий

Оператор While: Невозможно заставить работать соединение нескольких условий

Я пытаюсь разобрать аргументы по 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в левой части возвращается ложный статус, результатиуже известно, а правая часть пропускается. Аналогично для ||и правдивого ретурха. Вот что &&и ||делают.

Но вы не дали никакого контекста этому вопросу, т. е. почему для вас имеет значение, если функция не вызывается, поэтому сложно придумать лучшие способы сделать это.

Связанный контент