在Bash中模拟do-while循环


136

在Bash中模拟do-while循环的最佳方法是什么?

我可以在进入while循环之前检查条件,然后继续在循环中重新检查条件,但这是重复的代码。有没有更清洁的方法?

我的脚本的伪代码:

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

check_if_file_present如果在$cutoff时间之后启动,则不会执行此操作,并且会在一段时间后启动。


您在寻找until开关吗?
Michael Gardner

2
@MichaelGardner until还将在执行循环主体之前评估条件
Alex

2
啊,我明白了,我误解了你的说法。
迈克尔·加德纳

Answers:


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是一个:内置的实际程序。前者简单地退出0(和false1),后者也绝对没有什么。您可以通过进行确认which true
Fleshgrinder

@Fleshgrinder :仍然可以代替trueBash使用。尝试一下while :; do echo 1; done
Alexej Magura

2
从来没有说过什么不同,只是那true不是Bash内置的。这是通常在中找到的程序/bin
Fleshgrinder

type true在bash中(一直回到bash 3.2)返回true is a shell builtin。的确/bin/true是一个程序。关于true的不正确之处在于true它不是内置的。(tl; dr:true是内置的bash和程序)
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'

由于您似乎在比较整数,因此我将测试更改为使用双括号。在双方括号内,比较运算符(例如<=lexical)是词法的,例如,在比较2和10时将给出错误的结果。这些运算符不在单个方括号内工作。


相当于单行while { check_if_file_present; ((current_time<=cutoff)); }; do :; done吗?即while条件内的命令是否有效地用分号隔开,而不用例如分隔&&,并用{}?分组?
Ruslan

@Ruslan:不需要大括号。您不应该使用&&或将任何内容链接到双括号内||的测试,因为这样可以有效地使它们成为控制的测试的一部分while。除非您在命令行上使用此构造,否则我不会将它作为单行代码(特别是在脚本中)使用,因为意图难以理解。
暂停,直到另行通知。

是的,我无意将其用作单行代码:只是为了阐明测试中的命令是如何连接的。我担心第一个返回非零的命令可能会使整个条件变为假。
Ruslan

3
@ruslan:不,这是最后一个返回值。while false; false; false; true; do echo here; break; done输出“ here”
暂停,直到另行通知。

@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"
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.