Sintaks skrip shell "for" loop

191

Saya mendapatkan yang berikut ini untuk bekerja:

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

Ini menghasilkan sekelompok baris output: 2, output: 3, seterusnya.

Namun, coba jalankan hal berikut:

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

menghasilkan yang berikut:

output: {2..10}

Bagaimana saya bisa membuat kompiler menyadari bahwa ia harus memperlakukan $ max sebagai ujung array, dan bukan bagian dari string?

eykanal
sumber
2
sistem dan shell apa yang Anda gunakan? Sistem konyol macam apa yang memiliki sh atau bash, tetapi tidak memiliki seq, sebuah coreutil?
whatsisname
11
FreeBSD tidak.
Nietzche-jou
echo "$iseharusnya echo "$i"- tidak akan memperbaiki masalah, meskipun.
Dave Jarvis
Gaya nit kecil: Saya biasanya melihat dodan thenkata kunci pada baris yang sama dengan fordan if, masing-masing. Misalnya,for i in {2..10}; do
kutu buku yang dibayar

Jawaban:

234

Perluasan brace, {x..y} dilakukan sebelum ekspansi lainnya, jadi Anda tidak bisa menggunakannya untuk urutan panjang variabel.

Sebaliknya, gunakan seq 2 $maxmetode sebagai pengguna mob dinyatakan .

Jadi, sebagai contoh Anda akan:

max=10
for i in `seq 2 $max`
do
    echo "$i"
done
Apa namanya
sumber
Tidak ada alasan yang baik untuk menggunakan perintah eksternal seperti sequntuk menghitung dan menambah angka dalam for loop, karena itu disarankan agar Anda tidak menggunakan seq. Perintah ini dibiarkan untuk kompatibilitas dengan bash lama. Perintah bawaan cukup cepat. for (( EXP1; EXP2; EXP3 )) ...
pabrik
13
@miller for (( ... ))sintaksnya bukan POSIX dan itu artinya misalnya tidak akan bekerja di luar kotak pada hal-hal seperti Alpine Linux. seqtidak.
Wernight
@ Wight Tidak Hanya Alpine Linux; #!/bin/shadalah Dash di Debian / Ubuntu.
Franklin Yu
72

Coba versi ekspresi aritmatika for:

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

Ini tersedia di sebagian besar versi bash, dan harus kompatibel dengan Bourne shell (sh).

sistem PAUSE
sumber
1
@Flow: Hm, saya baru mencobanya di beberapa sistem (berbasis Linux dan BSD) dengan #! / Bin / sh dan itu bekerja dengan baik. Dipanggil di bawah bash dan secara khusus di bawah / bin / sh, masih berfungsi. Mungkin versi sh penting? Apakah Anda menggunakan Unix lama?
system PAUSE
8
Sangat sedikit sistem yang memiliki dedicated sh, alih-alih menjadikannya tautan ke shell lain. Idealnya, shell seperti itu dipanggil karena hanyash akan mendukung fitur-fitur tersebut dalam standar POSIX, tetapi secara default membiarkan beberapa fitur tambahan melaluinya. C-style for-loop bukan fitur POSIX, tetapi mungkin dalam mode oleh shell yang sebenarnya. sh
chepner
27

Langkah loop secara manual:

i = 0
maks = 10
sementara [$ i -lt $ max]
melakukan
    echo "output: $ i"
    true $ ((i ++))
selesai

Jika Anda tidak harus benar-benar POSIX, Anda dapat menggunakan aritmatika untuk loop:

maks = 10
untuk ((i = 0; i <maks; i ++)); lakukan echo "output: $ i"; selesai

Atau gunakan jot (1) pada sistem BSD:

untuk saya dalam $ (jot 0 10); lakukan echo "output: $ i"; selesai
Nietzche-jou
sumber
3
true $(( i++ ))tidak berfungsi dalam semua kasus, jadi sebagian besar portabel mungkin true $((i=i+1)).
Aliran
titik koma tidak boleh berupa "for ((i = 0; i <max; i ++));"
logan
9

Ada lebih dari satu cara untuk melakukannya.

max=10
for i in `eval "echo {2..$max}"`
do
    echo "$i"
done
singkat
sumber
7
Risiko keamanan, karena evalakan mengevaluasi apa pun yang Anda tetapkan max. Pertimbangkan max="2}; echo ha; #", lalu ganti echo hadengan sesuatu yang lebih destruktif.
chepner
6
(S) ia menetapkan maksimal 10. Tidak ada risiko.
Conrad Meyer
9

Jika seqperintah tersedia di sistem Anda:

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

Jika tidak, maka gunakan pria miskin seqdengan perl:

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

Lihat tanda kutip itu.

massa
sumber
Saya baru saja memeriksanya; sepertinya tidak.
eykanal
5

Ini caranya:
Bash:

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

Cara Bash di atas akan bekerja untuk kshdan zshjuga, ketika bash -cdiganti dengan ksh -catauzsh -c masing masing.

Catatan: for i in {2..${max}}; do echo $i; doneberfungsi di zshdan ksh.

Jahid
sumber
4

Kita dapat mengulangi loop seperti pemrograman C.

#!/bin/bash
for ((i=1; i<=20; i=i+1))
do 
      echo $i
done
ruam
sumber
2

Ini berfungsi pada Mac OS X.

Ini termasuk contoh tanggal BSD, cara menambah dan mengurangi tanggal juga:

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

Yah, karena saya tidak seqmenginstal perintah pada sistem saya (Mac OS X v10.6.1 (Snow Leopard)), saya whilemalah menggunakan loop:

max=5
i=1

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

* Mengangkat bahu * Apapun yang berhasil.

eykanal
sumber
2
seq relatif baru. Saya baru tahu beberapa bulan yang lalu. Tapi Anda bisa menggunakan loop 'untuk' !! Kerugian dari 'sementara' adalah Anda harus ingat untuk menambah penghitung di suatu tempat di dalam loop, atau loop ke bawah.
system PAUSE
1

Semua ini dilakukan {1..8}dan semuanya POSIX. Mereka juga tidak akan rusak jika Anda menempatkan kondisional continuedalam loop. Cara kanonik:

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

Cara lain:

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

dan lainnya:

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

sumber
1

Menggunakan:

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

Anda memerlukan panggilan 'eval' eksplisit untuk mengevaluasi kembali {} setelah penggantian variabel.

Chris Dodd
sumber