Эмуляция цикла do-while в Bash

Каков наилучший способ эмуляции цикла do-while в Bash?

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

Псевдокод моего script:

while [ current_time <= $cutoff ]; do
    check_if_file_present
    #do other stuff
done

Это не выполняется check_if_file_present, если оно запущено после $cutoff времени, а do-while будет.

Ответ 1

Два простых решения:

  • Выполняйте свой код один раз перед циклом while

    actions() {
       check_if_file_present
       # Do other stuff
    }
    
    actions #1st execution
    while [ current_time <= $cutoff ]; do
       actions # Loop execution
    done
    
  • Или:

    while : ; do
        actions
        [[ current_time <= $cutoff ]] || break
    done
    

Ответ 2

Поместите тело вашей циклы после while и перед тестом. Фактическое тело цикла while должно быть неактивным.

while 
    check_if_file_present
    #do other stuff
    (( current_time <= cutoff ))
do
    :
done

Вместо двоеточия вы можете использовать continue, если вы найдете это более читабельным. Вы также можете вставить команду, которая будет выполняться только между итерациями (не до первой или после последней), например echo "Retrying in five seconds"; sleep 5. Или распечатайте разделители между значениями:

i=1; while printf '%d' "$((i++))"; (( i <= 4)); do printf ','; done; printf '\n'

Я изменил тест, чтобы использовать двойные скобки, так как вы, кажется, сравниваете целые числа. Внутри двойных квадратных скобок операторы сравнения, такие как <=, являются лексическими и будут давать неверный результат, например, при сравнении 2 и 10. Эти операторы не работают внутри одинарных квадратных скобок.

Ответ 3

  Вместо двоеточия вы можете использовать продолжить, если вы найдете, что более читабельным

Этот метод имитирует способ выполнения циклов Do-While, который я искал - чтобы иметь тест состояния после действий. Я также обнаружил, что я могу поместить приращение индекса (если необходимо) после оператора do вместо оператора continue.

while 
    check_if_file_present
    #do other stuff
    (( current_time <= cutoff ))
do
    (( index++ ));
done

Ответ 4

Мы можем эмулировать цикл выполнения в Bash с помощью while [[condition]]; do true; done следующим образом:

while [[ current_time <= $cutoff ]]
    check_if_file_present
    #do other stuff
do true; done

Для примера. Вот моя реализация получения ssh-соединения в bash-скрипте:

#!/bin/bash
while [[ $STATUS != 0 ]]
    ssh-add -l &>/dev/null; STATUS="$?"
    if [[ $STATUS == 127 ]]; then echo "ssh not instaled" && exit 0;
    elif [[ $STATUS == 2 ]]; then echo "running ssh-agent.." && eval 'ssh-agent' > /dev/null;
    elif [[ $STATUS == 1 ]]; then echo "get session identity.." && expect $HOME/agent &> /dev/null;
    else ssh-add -l && git submodule update --init --recursive --remote --merge && return 0; fi
do true; done

Он выдаст выходные данные в следующей последовательности:

Step #0 - "gcloud": intalling expect..
Step #0 - "gcloud": running ssh-agent..
Step #0 - "gcloud": get session identity..
Step #0 - "gcloud": 4096 SHA256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX /builder/home/.ssh/id_rsa (RSA)
Step #0 - "gcloud": Submodule '.google/cloud/compute/home/chetabahana/.docker/compose' ([email protected]:chetabahana/compose) registered for path '.google/cloud/compute/home/chetabahana/.docker/compose'
Step #0 - "gcloud": Cloning into '/workspace/.io/.google/cloud/compute/home/chetabahana/.docker/compose'...
Step #0 - "gcloud": Warning: Permanently added the RSA host key for IP address 'XXX.XX.XXX.XXX' to the list of known hosts.
Step #0 - "gcloud": Submodule path '.google/cloud/compute/home/chetabahana/.docker/compose': checked out '24a28a7a306a671bbc430aa27b83c09cc5f1c62d'
Finished Step #0 - "gcloud"