zsh альтернативный вывод while-цикла неожиданного рода

Вопрос или проблема

У меня ZSH_VERSION=5.9 и есть этот скрипт, который работает как ожидается:

integer count=3
while true ; do
    echo $count
    (( count-- ))
    if (( count == 0 )) break
done
echo Наконец: $count
echo ok

Он считает от 3 до 1 и затем выводит: Наконец: 0 и ok.

Но давайте заменим блок do … done на { … } вот так:

integer count=3
while true ; {
    echo $count
    (( count-- ))
    if [[ $count == 0 ]] break
}
echo Наконец: $count
echo ok

Абсолютно никаких сообщений об ошибке, но вывод сбивает с толку:

3
Наконец: 2
ok
2
Наконец: 1
ok
1

У меня абсолютно нет идеи, почему это может происходить, кроме как проблема с парсером. Или я неправильно использую альтернативный синтаксис while-цикла? Это происходит, когда я нахожусь в только что запущенном shell.

Ответ или решение

Ваша проблема с использованием альтернативного синтаксиса циклов while в языке командного интерпретатора Zsh заключается в том, что вы неправильно поняли, как обрабатываются группы команд в фигурных скобках { ... }. Рассмотрим, почему ваш скрипт ведет себя неожиданно и как исправить эту ситуацию.

Теория

В языке Zsh, как и в других интерпретаторах, существует несколько способов группировки команд. В стандартном варианте do ... done команды в цикле выполняются как целостное выражение, плотно интегрированное в структуру цикла while. Этот способ является аналогом базового синтаксиса, знакомого большинству пользователей, и подразумевает последовательное выполнение команд до тех пор, пока условие цикла истинно.

Однако, когда вы используете блок в фигурных скобках { ... }, существует технический нюанс: команды внутри такого блока выполняются без создания отдельного под-shell или подгруппы, но семантически они не являются частью цикла, как это происходит с do ... done. Это может вызвать неожиданные эффекты в поведении скрипта.

Пример

Рассмотрим ваш код:

integer count=3
while true ; {
    echo $count
    (( count-- ))
    if [[ $count == 0 ]] break
}
echo Finally: $count
echo ok

Вы используете фигурные скобки для обозначения блока, но при этом объединение цикла и блока while/{ ... } требует дополнительного разделения, такого как точка с запятой или другая подходящая структура для завершения. В результате, код не обрабатывает break внутри блока так, как ожидалось, что приводит к неожиданным результатам.

Применение

Для корректной работы вашего скрипта можно воспользоваться двумя подходами:

  1. Возврат к базовому синтаксису: Сделайте использование стандартного do ... done, чтобы правильно интегрировать выполняемый блок в цикл while.

  2. Использование функции: Вместо использования { ... } вы можете обернуть блок кода в функцию, что позволит явно контролировать логику выполнения и использовать встроенные возможности структурного программирования ZSH.

Пример возвращения к базовому синтаксису:

integer count=3
while true ; do
    echo $count
    (( count-- ))
    if (( count == 0 )) break
done
echo Finally: $count
echo ok

Пример использования функции:

integer count=3

perform_countdown() {
  echo $count
  (( count-- ))
  if (( count == 0 )); then
    return 1  # Вернуть код ошибки, обозначающий завершение цикла
  else
    return 0  # Продолжаем выполнение цикла
  fi
}

while true; do
  perform_countdown || break
done
echo Finally: $count
echo ok

В данном подходе мы создали функцию perform_countdown, которая выполняет все действия цикла и возвращает код выхода, используемый для управления потоком выполнения.

Таким образом, корректное понимание работы фигурных скобок и использование структуры while do ... done или функций позволяет управлять интерпретатором Zsh более гибко и предсказуемо. Ваша исходная проблема, к сожалению, обоснована недоразумением в реализации синтаксиса оболочки и может быть решена через соблюдение этих принципов.

Оцените материал
Добавить комментарий

Капча загружается...