Емуляція циклу виконання часу в Bash


137

Який найкращий спосіб емуляції циклу виконання часу в Bash?

Я міг перевірити стан перед входом у whileцикл, а потім продовжувати повторну перевірку стану в циклі, але це дублюваний код. Чи є чистіший спосіб?

Псевдо-код мого сценарію:

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

Це не виконується, check_if_file_presentякщо його запустити після закінчення $cutoffчасу, і час, який би відбувся, буде.


Шукаєте untilвимикач?
Майкл Гарднер

2
@MichaelGardner untilтакож оцінить стан перед виконанням тіла петлі
Алекс

2
Ах, бачу, я неправильно зрозумів твою капризу.
Майкл Гарднер

Відповіді:


216

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

  1. Виконайте свій код один раз перед циклом while

    actions() {
       check_if_file_present
       # Do other stuff
    }
    
    actions #1st execution
    while [ current_time <= $cutoff ]; do
       actions # Loop execution
    done
  2. Або:

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

9
:- це вбудований, еквівалентний вбудованому true. Вони обоє "нічого не роблять успішно".
loxaxs

2
@loxaxs, що відповідає дійсності, наприклад, zsh, але не в Bash. true- це фактична програма, тоді :як вбудована. Перший просто виходить з 0falseз 1) останній абсолютно нічого не робить. Ви можете перевірити which true.
Fleshgrinder

@Fleshgrinder :все ще може бути використаний замість trueБаша. Спробуйте це while :; do echo 1; done.
Олексій Магура

2
Ніколи не говорив нічого іншого, тільки trueце не вбудований в Bash. Це програма, яку зазвичай можна знайти в /bin.
Fleshgrinder

type trueв bash (все назад до bash 3.2) повертається true is a shell builtin. Це правда, що /bin/trueце програма; що не відповідає правді, це те, що trueце не вбудований. (tl; dr: true is bash buildin AND program)
PJ Eby

152

Помістіть тіло петлі після 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. Ці оператори не працюють у квадратних дужках.


Чи еквівалентно однолінійному while { check_if_file_present; ((current_time<=cutoff)); }; do :; done? Тобто команди всередині whileумови ефективно розділяються крапками з комою, а не, наприклад &&, і групуються по {}?
Руслан

@Ruslan: фігурні брекети непотрібні. Вам не слід пов'язувати нічого з тестом всередині подвійних дужок, використовуючи &&або ||оскільки це фактично робить їх частиною тесту, який контролює while. Якщо ви не використовуєте цю конструкцію в командному рядку, я б не робив це як однолінійний (зокрема в сценарії), оскільки намір не читається.
Призупинено до подальшого повідомлення.

Так, я не мав наміру використовувати його як однолінійку: просто для уточнення того, як з'єднані команди в тесті. Я хвилювався, що перша команда, що повертається не-нульовою, може зробити всю умову помилковою.
Руслан

3
@ruslan: Ні, це останнє повернене значення. while false; false; false; true; do echo here; break; doneвиходи "тут"
Призупинено до подальшого повідомлення.

@thatotherguy: Це між можливостями досить здорово! Ви також можете використовувати його для вставки роздільника в рядок. Дякую!
Призупинено до подальшого повідомлення.

2

Ми можемо наслідувати цикл виконання часу в Bash while [[condition]]; do true; doneтаким чином:

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

Для прикладу. Ось моя реалізація щодо отримання ssh-з'єднання в bash script:

#!/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' (git@github.com: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"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.