เชลล์สคริปต์“ for” ไวยากรณ์วนซ้ำ


191

ฉันได้งานต่อไปนี้แล้ว:

for i in {2..10}
do
    echo "output: $i"
done

มันผลิตเส้นจำนวนoutput: 2มากoutput: 3และอื่น ๆ

อย่างไรก็ตามพยายามที่จะเรียกใช้ต่อไปนี้:

max=10
for i in {2..$max}
do
    echo "$i"
done

ผลิตดังต่อไปนี้:

output: {2..10}

ฉันจะให้คอมไพเลอร์รู้ได้อย่างไรว่ามันควรจะให้ $ max เป็นส่วนท้ายของอาร์เรย์และไม่ใช่ส่วนหนึ่งของสตริง


2
คุณใช้ระบบและเปลือกอะไร ระบบโง่ ๆ แบบไหนที่มี SH หรือทุบตี แต่ไม่มี seq, coreutil?
whatsisname

11
FreeBSD ไม่ได้
Nietzche-jou

echo "$iควรจะเป็นecho "$i"- ไม่สามารถแก้ไขปัญหาได้
เดฟจาร์วิส

นิด ๆ หน่อย ๆ สไตล์: ฉันมักจะเห็นdoและthenคำหลักในบรรทัดเดียวกับforและifตามลำดับ เช่นfor i in {2..10}; do
เงินที่จ่ายเมื่อ

คำตอบ:


234

รั้งการขยายตัว{x .. y}จะดำเนินการก่อนการขยายตัวอื่น ๆ ดังนั้นคุณไม่สามารถใช้มันสำหรับลำดับความยาวตัวแปร

แต่ใช้seq 2 $maxวิธีการที่เป็นผู้ใช้ม็อบที่ระบุไว้

ดังนั้นสำหรับตัวอย่างของคุณมันจะเป็น:

max=10
for i in `seq 2 $max`
do
    echo "$i"
done

ไม่มีเหตุผลที่ดีที่จะใช้คำสั่งภายนอกเช่นที่เป็นอยู่ในการนับและตัวเลขที่เพิ่มขึ้นในการห่วงจึงจะแนะนำให้คุณหลีกเลี่ยงการใช้seq seqคำสั่งนี้เหลือสำหรับความเข้ากันได้กับ bash เก่า คำสั่งในตัวนั้นเร็วพอ for (( EXP1; EXP2; EXP3 )) ...
มิลเลอร์

13
@miller for (( ... ))ไวยากรณ์ไม่ใช่ POSIX และนั่นหมายความว่ามันจะไม่ทำงานนอกกรอบในสิ่งต่างๆเช่น Alpine Linux seqทำ.
Wernight

@ ความสว่างไม่เพียง แต่อัลไพน์ Linux; #!/bin/shเป็น Dash ใน Debian / Ubuntu
Franklin Yu

72

ลองใช้เวอร์ชันเลขคณิตนิพจน์ของfor:

max=10
for (( i=2; i <= $max; ++i ))
do
    echo "$i"
done

มีให้ใน bash รุ่นส่วนใหญ่และควรใช้ร่วมกับ Bourne shell (sh) ได้เช่นกัน


1
@ กระแส: หืมมฉันเพิ่งลองใช้ระบบสองสามระบบ (ใช้ Linux และ BSD) ด้วย #! / bin / sh และทำงานได้ดี เรียกใช้ภายใต้ bash และภายใต้ / bin / sh โดยเฉพาะยังคงทำงานได้ บางทีรุ่นของเรื่องสำคัญ? คุณอยู่ในระบบยูนิกซ์เก่าหรือไม่?
ระบบหยุดชั่วคราว

8
มีเพียงไม่กี่ระบบที่มีการทุ่มเทshแทนที่จะทำให้มันเป็นลิงค์ไปยังเชลล์อื่น จะเป็นการดีเช่นเปลือกเรียกว่าเป็นshจะเพียงสนับสนุนคุณสมบัติผู้ที่อยู่ในมาตรฐาน POSIX แต่โดยปกติให้บางส่วนของคุณสมบัติพิเศษของพวกเขาผ่าน For-loop แบบ C ไม่ใช่คุณสมบัติ POSIX แต่อาจอยู่ในshโหมดโดยเชลล์จริง
chepner

27

ขั้นตอนการวนซ้ำด้วยตนเอง:

i = 0
สูงสุด = 10
ในขณะที่ [$ i -lt $ สูงสุด]
ทำ
    echo "เอาต์พุต: $ i"
    จริง $ ((i ++))
เสร็จแล้ว

หากคุณไม่ต้อง POSIX ทั้งหมดคุณสามารถใช้เลขคณิตสำหรับการวนซ้ำ:

สูงสุด = 10
สำหรับ ((i = 0; i <max; i ++)); ทำเสียงสะท้อน "เอาท์พุท: $ i"; เสร็จแล้ว

หรือใช้jot (1) บนระบบ BSD:

สำหรับฉันใน $ (jot 0 10); ทำเสียงสะท้อน "เอาท์พุท: $ i"; เสร็จแล้ว

3
true $(( i++ ))ไม่สามารถใช้งานได้ในทุกกรณีดังนั้นอุปกรณ์พกพาส่วนใหญ่จึงเป็นtrue $((i=i+1))เช่นนั้น
Flow

เซมิโคลอนไม่ควรอยู่ใน "สำหรับ ((i = 0; i <max; i ++));"
โลแกน

9

มีมากกว่าหนึ่งวิธีที่จะทำ

max=10
for i in `eval "echo {2..$max}"`
do
    echo "$i"
done

7
ความเสี่ยงด้านความปลอดภัยเนื่องจากevalจะประเมินอะไรที่คุณตั้งไว้maxเพื่อ พิจารณาmax="2}; echo ha; #"แล้วแทนที่echo haด้วยสิ่งที่ทำลายล้างมากขึ้น
chepner

6
(S) เขาตั้งค่าสูงสุดเป็น 10 ไม่มีความเสี่ยง
Conrad Meyer

9

หากseqคำสั่งมีอยู่ในระบบของคุณ:

for i in `seq 2 $max`
do
  echo "output: $i"
done

ถ้าไม่ใช่ให้ใช้คนจนseqกับperl:

seq=`perl -e "\$,=' ';print 2..$max"`
for i in $seq
do
  echo "output: $i"
done

ดูเครื่องหมายคำพูดเหล่านั้น


ฉันเพิ่งตรวจสอบสิ่งนั้น ดูเหมือนจะไม่เป็นเช่นนั้น
eykanal

5

นี่คือวิธี:
Bash:

max=10
for i in $(bash -c "echo {2..${max}}"); do echo $i; done

วิธีทุบตีดังกล่าวจะทำงานkshและzshเช่นกันเมื่อbash -cถูกแทนที่ด้วยksh -cหรือzsh -cตามลำดับ

หมายเหตุ: for i in {2..${max}}; do echo $i; doneผลงานในและzshksh



2

ที่นี่ทำงานได้บน Mac OS X

มันมีตัวอย่างของวันที่ BSD วิธีการเพิ่มและลดวันที่:

for ((i=28; i>=6 ; i--));
do
    dat=`date -v-${i}d -j "+%Y%m%d"` 
    echo $dat
done

2

ตอนที่ฉันไม่ได้seqติดตั้งคำสั่งบนระบบของฉัน (Mac OS X v10.6.1 (Snow Leopard)) ฉันก็เลยใช้whileloop แทน:

max=5
i=1

while [ $max -gt $i ]
do
    (stuff)
done

* ยักไหล่ * อะไรก็ได้


2
seq ค่อนข้างใหม่ ฉันเพิ่งทราบเกี่ยวกับเรื่องนี้ไม่กี่เดือนที่ผ่านมา แต่คุณสามารถใช้ลูป 'for' ได้ !! ข้อเสียของ 'ขณะที่' คือคุณต้องจำไว้ว่าต้องเพิ่มตัวนับที่ใดที่หนึ่งในวงหรือมิฉะนั้นวนลง
ระบบหยุดชั่วคราว

1

สิ่งเหล่านี้ทำ{1..8}และควรเป็น POSIX พวกเขาจะไม่แตกถ้าคุณใส่เงื่อนไขcontinueในลูป วิธีบัญญัติ:

f=
while [ $((f+=1)) -le 8 ]
do
  echo $f
done

อีกวิธีหนึ่ง:

g=
while
  g=${g}1
  [ ${#g} -le 8 ]
do
  echo ${#g}
done

เเละอีกอย่าง:

set --
while
  set $* .
  [ ${#} -le 8 ]
do
  echo ${#}
done

1

ใช้:

max=10
for i in `eval echo {2..$max}`
do
    echo $i
done

คุณต้องมีการเรียก 'eval' อย่างชัดเจนเพื่อประเมินค่า {} ใหม่หลังจากการแทนที่ตัวแปร

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