การจำลองลูป do-while ใน Bash


137

วิธีที่ดีที่สุดในการเลียนแบบลูป do-while ใน Bash คืออะไร

ฉันสามารถตรวจสอบเงื่อนไขก่อนที่จะเข้าสู่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
อ่าฉันเข้าใจฉันเข้าใจผิดคุณ
Michael Gardner

คำตอบ:


216

สองวิธีง่ายๆ:

  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

2
@loxaxs ที่เป็นจริงเช่น zsh แต่ไม่ใช่ใน Bash trueเป็นโปรแกรมจริงในขณะที่:มีอยู่แล้วภายใน อดีตเพียงออกจากกับ0(และfalseด้วย1) หลังไม่ทำอะไรเลยอย่างแน่นอน which trueคุณสามารถตรวจสอบกับ
Fleshgrinder

@Fleshgrinder :ยังคงใช้งานได้trueใน Bash while :; do echo 1; doneลองกับ
Alexej Magura

2
ไม่เคยพูดอะไรที่แตกต่างออกไป แต่นั่นtrueไม่ใช่แบบในตัวของ Bash /binมันเป็นโปรแกรมที่มักจะพบใน
Fleshgrinder

type trueในทุบตี (ทุกทางกลับไปทุบตี 3.2) true is a shell builtinผลตอบแทน มันเป็นความจริงที่/bin/trueเป็นโปรแกรม สิ่งที่ไม่จริงเกี่ยวกับความจริงก็trueคือไม่ใช่ในตัว (tl; dr: true เป็น bash builtin และโปรแกรม)
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

@Ruslan: การจัดฟันแบบหยิกไม่จำเป็น คุณไม่ควรเชื่อมโยงอะไรไปทดสอบภายในวงเล็บคู่ใช้&&หรือตั้งแต่ที่มีประสิทธิภาพทำให้เป็นส่วนหนึ่งของการทดสอบพวกเขาว่าการควบคุม|| whileถ้าคุณไม่ใช้โครงสร้างนี้ในบรรทัดคำสั่งฉันจะไม่ทำมันเป็นหนึ่งซับ (ในสคริปต์โดยเฉพาะ) เนื่องจากเจตนาไม่สามารถอ่านได้
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

ใช่ฉันไม่ได้ตั้งใจจะใช้เป็นสายการบินเดียว: เพียงเพื่อชี้แจงว่าคำสั่งในการทดสอบเชื่อมต่อกันอย่างไร ฉันกังวลว่าคำสั่งแรกที่ส่งคืนค่าที่ไม่ใช่ศูนย์อาจทำให้เงื่อนไขทั้งหมดเป็นเท็จ
Ruslan

3
@ruslan: ไม่มันคือค่าตอบแทนสุดท้าย while false; false; false; true; do echo here; break; doneเอาท์พุท "ที่นี่"
หยุดชั่วคราวจนกว่าจะมีประกาศ

@thatotherguy: นั่นระหว่างความสามารถในการเป็นเย็นสวย! คุณสามารถใช้มันเพื่อแทรกตัวคั่นในสตริง ขอบคุณ!
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

2

เราสามารถเลียนแบบลูป do-while ใน Bash ด้วยwhile [[condition]]; do true; doneวิธีนี้:

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

สำหรับตัวอย่าง นี่คือการดำเนินการของฉันในการรับการเชื่อมต่อ 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.