ใน bash เป็นไปได้ไหมที่จะใช้ตัวแปรจำนวนเต็มในการควบคุมลูปของ for for loop


65

ฉันมีสคริปต์ทุบตีต่อไปนี้:

#!/bin/bash

upperlim=10

for i in {0..10}
do
echo $i
done

for i in {0..$upperlim}
do
echo $i
done

การforวนซ้ำครั้งแรก( โดยไม่มีตัวแปรupperlimในการควบคุมการวนซ้ำ) ทำงานได้ดี แต่การforวนซ้ำครั้งที่สอง( ด้วยตัวแปรupperlimในการควบคุมการวนซ้ำ) ไม่ทำงาน มีวิธีใดบ้างที่ฉันสามารถแก้ไขforลูปที่สองเพื่อให้ทำงานได้? ขอบคุณที่สละเวลา.


4
for i in {0..$((upperlim))}; do echo $i; done
อืม

และ +1 เพราะฉันพบว่าพฤติกรรมน่าสนใจ
Bonsi Scott

ไซต์ข้ามที่เป็นไปได้ซ้ำซ้อนของ: stackoverflow.com/questions/169511/ …
Ciro Santilli 新疆改造中心中心法轮功六四事件

ลิงก์ภายนอกที่ตอบคำถามนี้: cyberciti.biz/faq/…
kon Psych

คำตอบ:


62

เหตุผลนี้คือลำดับที่สิ่งต่าง ๆ เกิดขึ้นใน bash การขยายของ Brace เกิดขึ้นก่อนที่ตัวแปรจะถูกขยาย เพื่อให้บรรลุเป้าหมายของคุณคุณต้องใช้ C-style สำหรับลูป:

upperlim=10

for ((i=0; i<=upperlim; i++)); do
   echo "$i"
done

1
และทำงานให้zshเช่นกัน ( แต่ไม่ได้สำหรับcsh, tcsh)
คณิตศาสตร์

29

ในการทำสิ่งนี้ให้เสร็จในสไตล์ของคุณโดยไม่ต้องใช้อะไรนอกจากบิวด์อินคุณจะต้องใช้ Eval:

d=12

for i in `eval echo {0..$d}`
do
echo $i
done

แต่ด้วยseq:

lowerlimit=0
upperlimit=12

for i in $(seq $lowerlimit $upperlimit)
do
echo $i
done

โดยส่วนตัวแล้วฉันพบว่าการใช้งานseqนั้นง่ายต่อการอ่านมากขึ้น


สำหรับ "built-in" seqเป็นคำสั่งภายนอกและไม่พร้อมใช้งานทุกที่ที่เป็น bash
jordanm

9
@ jordanm: สำหรับการใช้ builtins ทั้งหมดพร้อมกับ bash จากนั้นฉันก็พูดว่า "แต่กับseq" โดยยอมรับว่ามันไม่ใช่แบบในตัว
Jodie C

ความจริงที่ว่าการขยายตัวรั้งนั้นมีอยู่ภายในนั้นไม่ใช่ปัญหาที่นี่ readเป็น builtin เช่น แต่ไม่มีเหตุผลที่จะevalมัน
จอร์แดน

1
Builtins ไม่เป็นปัญหาเลย ฉันต้องการที่จะให้บริการโซลูชั่นทุบตีทั้งหมดสำหรับผู้ถาม หากคุณต้องการโต้เถียงเกี่ยวกับเรื่องนี้เอาไปแชท; ความคิดเห็นไม่ดีสำหรับสิ่งประเภทนี้
Jodie C

8

วิธี POSIX

หากคุณสนใจเกี่ยวกับการพกพาให้ใช้ตัวอย่างจากมาตรฐาน POSIX :

i=2
END=5
while [ $i -le $END ]; do
    echo $i
    i=$(($i+1))
done

เอาท์พุท:

2
3
4
5

สิ่งที่ไม่ใช่ POSIX:

  • (( ))โดยไม่ต้องดอลลาร์แม้ว่ามันจะเป็นส่วนขยายที่พบดังกล่าวโดย POSIX ตัวเอง
  • [[. [ก็เพียงพอแล้ว ดูเพิ่มเติมที่: https://stackoverflow.com/questions/13542832/bash-if-difference-between-square-brackets-and-double-square-brackets
  • for ((;;))
  • seq
  • {start..end}และที่ไม่สามารถทำงานร่วมกับตัวแปรตามที่กล่าวไว้โดยคู่มือการทุบตี
  • let i=i+1: POSIX 7 2. ภาษาคำสั่งของเชลล์ไม่มีคำศัพท์letและจะล้มเหลวในbash --posix4.3.42
  • i=$i+1อาจจำเป็นต้องใช้เงินดอลลาร์แต่ฉันไม่แน่ใจ POSIX 7 2.6.4 การขยายตัวทางคณิตศาสตร์พูดว่า:

    หากตัวแปรเชลล์xมีค่าที่เป็นค่าคงที่จำนวนเต็มที่ถูกต้องเลือกที่จะรวมเครื่องหมายนำหน้าหรือเครื่องหมายบวกดังนั้นการขยายเลขคณิต"$((x))"และ"$(($x))"จะคืนค่าเดิม

    แต่อ่านมันอย่างแท้จริงที่ไม่ได้หมายความว่า$((x+1))ขยายเนื่องจากx+1ไม่ใช่ตัวแปร


1

วิธีการของคุณจะไม่ทำงานเนื่องจากใน bash brace-expansion เกิดขึ้นก่อนการขยายพารามิเตอร์ คุณต้องขยายตัวแปรก่อน

คุณสามารถหลีกเลี่ยง eval :

upperlim=10
eval '
        for i in {0..'"$upperlim"'}
        do
                echo $i
        done
'

ด้วยขณะที่ลูป :

upperlim=10
#with while
start=0
while [[ $start -le $upperlim ]]
do
    echo "$start"
    ((start = start + 1))
done

นอกจากนี้คุณสามารถทำได้ด้วยคำสั่ง seq :

upperlim=10
#seq
for i in $(seq "$upperlim"); do
  echo "$i"
done

หากคุณต้องการใช้งานด้วยfor i in {0..$upperlim}คุณจะต้องใช้ kornshell เช่น:

#!/bin/ksh
upperlim=10

for i in {0..$upperlim}
do
        echo $i
done
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.