Mô phỏng một vòng lặp do-while trong Bash


137

Cách tốt nhất để mô phỏng một vòng lặp do-while trong Bash là gì?

Tôi có thể kiểm tra điều kiện trước khi vào whilevòng lặp, và sau đó tiếp tục kiểm tra lại điều kiện trong vòng lặp, nhưng đó là mã trùng lặp. Có cách nào sạch hơn không?

Mã giả của tập lệnh của tôi:

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

Điều này không thực hiện check_if_file_presentnếu được khởi chạy sau $cutoffthời gian và một thời gian sẽ làm.


Bạn đang tìm kiếm công untiltắc?
Michael Gardner

2
@MichaelGardner untilcũng sẽ đánh giá tình trạng trước khi thực hiện phần thân của vòng lặp
Alex

2
Ah, tôi hiểu rồi, tôi đã hiểu nhầm sự ngớ ngẩn của bạn.
Michael Gardner

Câu trả lời:


216

Hai giải pháp đơn giản:

  1. Thực thi mã của bạn một lần trước vòng lặp while

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

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

9
:là một tích hợp, tương đương với tích hợp true. Cả hai "không làm gì thành công".
loxaxs

2
@loxaxs đúng trong ví dụ zsh nhưng không phải ở Bash. truelà một chương trình thực tế trong khi :được tích hợp sẵn. Cái trước chỉ đơn giản là thoát với 0(và falsevới 1) cái sau hoàn toàn không có gì. Bạn có thể kiểm tra với which true.
F Meatgrinder

@F Meatgrinder :vẫn có thể sử dụng thay cho trueBash. Hãy thử nó với while :; do echo 1; done.
Alexej Magura

2
Không bao giờ nói bất cứ điều gì khác nhau, chỉ có điều đó truekhông phải là tích hợp trong Bash. Đó là một chương trình thường được tìm thấy trong /bin.
Xác thịt

type truetrong bash (tất cả các cách quay lại bash 3.2) trả về true is a shell builtin. Đúng /bin/truelà một chương trình; Điều không đúng về sự thật là đó truekhông phải là nội dung. (tl; dr: true là một bash dựng sẵn VÀ một chương trình)
PJ Eby

152

Đặt phần thân của vòng lặp của bạn sau whilevà trước khi kiểm tra. Cơ thể thực sự của whilevòng lặp nên là một no-op.

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

Thay vì dấu hai chấm, bạn có thể sử dụng continuenếu bạn thấy nó dễ đọc hơn. Bạn cũng có thể chèn một lệnh sẽ chỉ chạy giữa các lần lặp (không phải trước hoặc trước sau), chẳng hạn như echo "Retrying in five seconds"; sleep 5. Hoặc in dấu phân cách giữa các giá trị:

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

Tôi đã thay đổi thử nghiệm để sử dụng dấu ngoặc kép vì bạn dường như đang so sánh các số nguyên. Bên trong dấu ngoặc kép, các toán tử so sánh như <=là từ vựng và sẽ cho kết quả sai khi so sánh 2 và 10 chẳng hạn. Các toán tử này không hoạt động trong dấu ngoặc vuông.


Nó có tương đương với một dòng while { check_if_file_present; ((current_time<=cutoff)); }; do :; donekhông? Tức là các lệnh bên trong whileđiều kiện được phân tách hiệu quả bằng dấu chấm phẩy không bằng &&, và được nhóm bởi {}?
Ruslan

@Ruslan: Niềng răng xoăn là không cần thiết. Bạn không nên liên kết bất cứ điều gì với bài kiểm tra bên trong dấu ngoặc kép bằng cách sử dụng &&hoặc ||vì điều đó thực sự khiến chúng trở thành một phần của bài kiểm tra kiểm soát while. Trừ khi bạn đang sử dụng cấu trúc này trên dòng lệnh, tôi sẽ không thực hiện dưới dạng một lớp lót (cụ thể là trong tập lệnh) vì mục đích không thể đọc được.
Tạm dừng cho đến khi có thông báo mới.

Vâng, tôi không có ý định sử dụng nó như một lớp lót: chỉ để làm rõ cách các lệnh trong thử nghiệm được kết nối. Tôi đã lo lắng rằng lệnh đầu tiên trả về giá trị khác không có thể khiến toàn bộ điều kiện sai.
Ruslan

3
@ruslan: Không, đó là giá trị trả lại cuối cùng. while false; false; false; true; do echo here; break; doneđầu ra "ở đây"
Tạm dừng cho đến khi có thông báo mới.

@thatotherguy: Giữa khả năng là khá tuyệt! Bạn cũng có thể sử dụng nó để chèn một dấu phân cách trong một chuỗi. Cảm ơn!
Tạm dừng cho đến khi có thông báo mới.

2

Chúng ta có thể mô phỏng một vòng lặp do-while trong Bash while [[condition]]; do true; donenhư thế này:

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

Ví dụ. Đây là cách tôi thực hiện để nhận kết nối ssh trong tập lệnh 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

Nó sẽ cho đầu ra theo trình tự như sau:

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"
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.