Bashでdo-whileループをエミュレートする


137

Bashでdo-whileループをエミュレートする最良の方法は何ですか?

whileループに入る前に条件をチェックしてから、ループで条件の再チェックを続けることができますが、それはコードの重複です。よりクリーンな方法はありますか?

私のスクリプトの疑似コード:

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

これはcheck_if_file_present$cutoff時間後に起動された場合は実行されませんが、しばらくの間は実行されます。


untilスイッチをお探しですか?
マイケルガードナー

2
@MichaelGardner untilは、ループの本体を実行する前に条件も評価します
Alex

2
ああ、なるほど、私はあなたの言い分を誤解しました。
Michael Gardner 2013年

回答:


216

2つの簡単なソリューション:

  1. whileループの前にコードを1回実行する

    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 2017

2
@loxaxsは、zshなどでは真ですが、Bashでは真ではありません。true:組み込みですが、実際のプログラムです。前者は単純で終了0(とfalse1、後者は絶対に何もしません)。で確認できwhich trueます。
Fleshgrinder

@Fleshgrinder :true、Bashの代わりに引き続き使用できます。でお試しくださいwhile :; do echo 1; done
Alexej Magura

2
それtrueはBashに組み込まれているわけではありません。これは通常にあるプログラム/binです。
Fleshgrinder

type truebashでは(bash 3.2に戻るまで)、を返しますtrue is a shell builtin。それ/bin/trueがプログラムであることは事実です。真実について真実でtrueはないことは、それが組み込みではないということです。(tl; dr:trueはbash組み込みANDプログラムです)
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ます。コマンドラインでこの構成を使用しているのでない限り、意図が判読できないため、(特にスクリプト内で)ワンライナーとしては使用しません。
追って通知があるまで一時停止。

ええ、私はそれをワンライナーとして使用するつもりはありませんでした:テストのコマンドがどのように接続されているかを明確にするためだけです。最初のコマンドがゼロ以外を返すと、条件全体が偽になる可能性があることを心配していました。
Ruslan

3
@ruslan:いいえ、これは最後の戻り値です。while false; false; false; true; do echo here; break; done「ここ」の出力
追って通知があるまで一時停止。

@thatotherguy:機能のあいだはかなりクールです!これを使用して、文字列に区切り文字を挿入することもできます。ありがとう!
追って通知があるまで一時停止。

2

次のwhile [[condition]]; do true; doneようにして、Bashでdo-whileループをエミュレートできます。

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

例として。これがbashスクリプトでssh接続を取得するための私の実装です:

#!/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"
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.