Bash'de bir do-while döngüsü taklit etme


137

Bash'te bir do-while döngüsü taklit etmenin en iyi yolu nedir?

Döngüye girmeden önce durumu kontrol edebilirim whileve daha sonra döngüdeki koşulu tekrar kontrol etmeye devam edebilirim, ancak bu yinelenen kod. Daha temiz bir yol var mı?

Senaryomun sahte kodu:

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

Bu check_if_file_present, bir $cutoffsüre sonra başlatıldığında gerçekleşmez ve bir süre gerekir.


Aradığınız untilanahtar?
Michael Gardner

2
@MichaelGardner until, döngü gövdesini çalıştırmadan önce durumu da değerlendirecek
Alex

2
Ah, anlıyorum, ocağınızı yanlış anladım.
Michael Gardner

Yanıtlar:


216

İki basit çözüm:

  1. Kodunuzu while döngüsünden önce bir kez yürütün

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

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

9
:yerleşik, yerleşik yerleşik eşdeğerdir true. İkisi de "başarılı bir şekilde hiçbir şey yapmıyor".
loxaxs

2
@loxaxs, örneğin zsh için geçerli ancak Bash için geçerli değil. yerleşik truebir programdır :. Birincisi basitçe çıkar 0(ve falseonunla 1) ikincisi kesinlikle hiçbir şey yapmaz. İle kontrol edebilirsiniz which true.
Fleshgrinder

@Fashhgrinder :, trueBash yerine hala kullanılabilir . İle deneyin while :; do echo 1; done.
Alexej Magura

2
Asla farklı bir şey söylemedim, sadece bu trueBash'te yerleşik değil. Genellikle bulunan bir programdır /bin.
Fleshgrinder

type truebash (bash 3.2'ye kadar) geri döner true is a shell builtin. /bin/trueBir program olduğu doğrudur ; doğru hakkında doğru olmayan şey, truebir yerleşik değildir. (tl; dr: true bir bash yerleşik ve bir programdır)
PJ Eby

152

Döngünün gövdesini whiletestten sonra ve testten önce yerleştirin. whileDöngünün gerçek gövdesi no-op olmalıdır.

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

İki nokta üst üste yerine, continuedaha okunabilir bulursanız kullanabilirsiniz . Ayrıca, yalnızca yinelemeler arasında (ilk önce veya sondan sonra değil) çalışacak bir komut ekleyebilirsiniz echo "Retrying in five seconds"; sleep 5. Veya değerler arasında sınırlayıcılar yazdırın:

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

Tam sayıları karşılaştırdığınız için testi çift parantez kullanacak şekilde değiştirdim. Çift köşeli parantezlerin içinde, örneğin karşılaştırma operatörleri <=sözcükseldir ve örneğin 2 ve 10'u karşılaştırırken yanlış sonuç verecektir. Bu operatörler tek köşeli parantez içinde çalışmaz.


Tek hatta while { check_if_file_present; ((current_time<=cutoff)); }; do :; donemı eşit ? Yani whilekoşul içindeki komutlar noktalı virgüllerle etkili bir şekilde birbirinden ayrılmaz &&ve {}?
Ruslan

@Ruslan: Kıvırcık parantezler gereksiz. Çift parantez içindeki teste hiçbir şey bağlamamalısınız &&veya ||bu etkili bir şekilde onları kontrol eden testin bir parçası haline getirmelidir while. Komut satırında bu yapıyı kullanmıyorsanız, niyet okunamaz olduğundan tek satırlık (özellikle bir komut dosyasında) yapmazdım.
sonraki duyuruya kadar duraklatıldı.

Evet, bunu tek astar olarak kullanmak istemiyordum: sadece testteki komutların nasıl bağlandığını açıklığa kavuşturmak için. Sıfır olmayan ilk komutun tüm koşulu yanlış yapabileceğinden endişeleniyordum.
Ruslan

3
@ruslan: Hayır, bu son dönüş değeridir. while false; false; false; true; do echo here; break; done"here" çıktıları
sonraki duyuruya kadar duraklatıldı.

@thatotherguy: Yetenek arasındaki bu oldukça güzel! Bir dizeye ayırıcı eklemek için de kullanabilirsiniz. Teşekkürler!
sonraki duyuruya kadar duraklatıldı.

2

Bash'te şu şekilde bir do-while döngüsü taklit edebiliriz while [[condition]]; do true; done:

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

Örnek olarak. İşte bash betiğinde ssh bağlantısı alma uygulamam :

#!/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

Çıkışı aşağıdaki gibi sırayla verecektir:

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"
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.