Bagaimana cara iterate atas kisaran angka yang didefinisikan oleh variabel-variabel di Bash?
Bagaimana cara iterate atas kisaran angka di Bash ketika kisaran yang diberikan oleh variabel?
Aku tahu aku bisa melakukan ini (yang disebut "urutan ekspresi" di Bash dokumentasi):
for i in {1..5}; do echo $i; done
Yang memberikan:
1
2
3
4
5
Namun, bagaimana saya bisa mengganti salah satu dari berbagai endpoint dengan variabel? Ini doesn't bekerja:
END=5
for i in {1..$END}; do echo $i; done
Yang mencetak:
{1..5}
1392
20
edit: saya lebih suka
seq
dibandingkan metode lain karena aku benar-benar ingat itu ;)The
seq
adalah metode yang paling sederhana, tetapi Bash memiliki built-in aritmatika evaluasi.Untuk ((expr1;expr2;expr3));
membangun bekerja sepertiuntuk (expr1;expr2;expr3)
di C dan bahasa yang sama, dan seperti yang lain((expr))
kasus, Bash memperlakukan mereka seperti aritmatika.diskusi
Menggunakan
seq
baik-baik saja, sebagai Jiaaro yang disarankan. Pax Diablo menyarankan Bash loop untuk menghindari memanggil subproses, dengan tambahan keuntungan yang lebih banyak memori cocok jika $END terlalu besar. Zathrus melihat sebuah bug yang khas dalam lingkaran pelaksanaan, dan juga mengisyaratkan bahwa sejakaku
adalah teks variabel, terus-menerus konversi untuk bolak-balik angka-angka dilakukan dengan terkait slow-down.hitung bilangan bulat
Ini adalah versi perbaikan dari Bash loop:
Berikut ini adalah mengapa ekspresi asli didn't bekerja.
Dari man bash:
Jadi, penjepit ekspansi adalah sesuatu yang dilakukan sejak dini sebagai murni tekstual makro operasi, sebelum parameter ekspansi.
Kerang yang sangat dioptimalkan hibrida antara makro prosesor dan lebih formal bahasa pemrograman. Dalam rangka mengoptimalkan penggunaan yang khas kasus, bahasa ini dibuat agak lebih kompleks dan beberapa keterbatasan yang diterima.
Saya akan menyarankan menempel dengan Posix1 fitur. Ini berarti menggunakan
for i in <daftar>; do
, jika daftar yang sudah diketahui, jika tidak, gunakansementara
atauseq
, seperti dalam:1. Bash adalah shell yang besar dan saya menggunakannya secara interaktif, tetapi saya don't menempatkan bash-isms ke script saya. Script mungkin perlu lebih cepat shell, lebih aman, lebih tertanam-gaya satu. Mereka mungkin perlu untuk berjalan pada apa pun yang diinstal seperti /bin/sh, dan kemudian ada semua biasa pro-standar argumen. Ingat *shellshock,* aka *bashdoor?*
POSIX cara
Jika anda peduli tentang portabilitas, gunakan contoh dari standar POSIX:
Output:
Hal-hal yang tidak POSIX:
(( ))
tanpa dolar, meskipun itu adalah umum ekstensi seperti yang disebutkan oleh POSIX sendiri.[[
.[
cukup di sini. Lihat juga: https://stackoverflow.com/questions/13542832/bash-if-difference-between-square-brackets-and-double-square-bracketsuntuk ((;;))
seq
(GNU Coreutils){mulai..end}
, dan yang tidak dapat bekerja dengan variabel-variabel seperti yang disebutkan oleh Bash manual.let i=i+1
: POSIX 7 2. Shell Command Language tidak mengandung katamari
, dan gagal dibash --posix
4.3.42i=$i+1
mungkin diperlukan, tapi saya'm tidak yakin. POSIX 7 2.6.4 Aritmatika Perluasan mengatakan:tapi membaca itu benar-benar itu tidak berarti bahwa
$((x+1))
mengembang sejakx+1
bukan sebuah variabel.Lapisan lain dari tipuan:
Anda dapat menggunakan
Jika anda membutuhkannya awalan dari anda mungkin seperti ini
Jika anda're di BSD / OS X anda dapat menggunakan iota bukan seq:
Ini bekerja dengan baik di
bash
:I've gabungan beberapa ide berikut ini dan diukur kinerjanya.
TL;DR Takeaways:
seq
dan{..}
benar-benar cepatuntuk
dansementara
loop lambat$( )
lambatuntuk (( ; ; ))
loop lambat$(( ))
ini bahkan lebih lambatIni bukan kesimpulan. Anda akan memiliki untuk melihat kode C di belakang masing-masing untuk menarik kesimpulan. Ini lebih tentang bagaimana kita cenderung untuk menggunakan masing-masing dari mekanisme ini untuk perulangan di atas kode. Paling satu operasi yang cukup dekat untuk menjadi kecepatan yang sama bahwa itu's tidak akan peduli dalam kebanyakan kasus. Tapi mekanisme seperti
untuk (( i=1; i<=1000000; i++ ))
lebih banyak operasi yang anda bisa melihat secara visual. Hal ini juga lebih banyak operasi per loop dari yang anda dapatkan darifor i in $(seq 1 1000000)
. Dan yang mungkin tidak jelas bagi anda, yang mengapa melakukan tes seperti ini sangat berharga.Demo
Aku tahu pertanyaan ini adalah tentang
bash
, tapi hanya untuk catatan -ksh93
lebih pintar dan mengimplementasikannya sesuai dengan yang diharapkan:Jika anda ingin tinggal sedekat mungkin dengan brace-sintaks ekspresi, mencoba
range
fungsi dari bash-trik'range.bash
.Sebagai contoh, berikut akan melakukan hal yang sama persis seperti
echo {1..10}
:Mencoba untuk dukungan asli bash sintaks dengan sedikit "yang perlu diperhatikan" mungkin: tidak hanya variabel didukung, tapi sering-perilaku yang tidak diinginkan yang tidak valid berkisar dipasok sebagai string (misalnya
for i in {1..a}; echo $i; dilakukan
) dicegah juga.Dengan jawaban yang lain akan bekerja pada sebagian besar kasus, tetapi mereka semua memiliki setidaknya satu dari kelemahan berikut:
seq
adalah biner yang harus diinstal untuk digunakan, harus dimuat oleh bash, dan harus berisi program yang anda harapkan, untuk itu untuk bekerja dalam kasus ini. Di mana-mana atau tidak, yang's lebih banyak mengandalkan dari sekedar Bash bahasa itu sendiri.{a..z}
; penjepit ekspansi akan. Pertanyaannya adalah tentang rentang numbers, meskipun, jadi ini adalah berdalih.{1..10}
brace-memperluas jangkauan sintaks, sehingga program-program yang menggunakan kedua mungkin akan sedikit sulit untuk dibaca.$END
variabel tidak valid range "bookend" untuk sisi lain dari jangkauan. JikaEND=a
, misalnya, kesalahan tidak akan terjadi dan verbatim nilai{1..a}
akan bergema. Ini adalah perilaku default Bash, juga-itu adalah hanya sering tak terduga.Disclaimer: saya penulis terkait kode.
Ini adalah cara lain:
Ganti
{}
dengan(( ))
:Ini semua bagus tapi seq diduga usang dan sebagian besar hanya bekerja dengan angka kisaran.
Jika anda menyertakan anda untuk loop dalam tanda kutip ganda, awal dan akhir variabel yang akan dereferenced ketika anda echo string, dan anda dapat mengirimkan string segera kembali ke BASH untuk eksekusi.
$i
harus melarikan diri dengan \'s sehingga TIDAK dievaluasi sebelum dikirim ke subkulit.Output ini juga dapat diberikan ke sebuah variabel:
Hanya "overhead" ini harus menghasilkan harus menjadi contoh kedua bash sehingga harus cocok untuk operasi intensif.
Jika anda're lakukan perintah shell dan anda (seperti saya) memiliki jimat untuk pipelining, yang satu ini lebih baik:
seq 1 $END | xargs -aku {} echo {}
Ada banyak cara untuk melakukan hal ini, namun orang-orang yang saya sukai adalah yang diberikan di bawah ini
Menggunakan
seq
Perintah penuh
seq pertama incr terakhir
Contoh:
Hanya dengan pertama dan terakhir:
Hanya dengan terakhir:
Menggunakan
{pertama..lalu..incr}
Di sini pertama dan terakhir yang wajib dan incr adalah opsional
Menggunakan hanya pertama dan terakhir
Menggunakan incr
Anda dapat menggunakan ini bahkan untuk karakter seperti di bawah ini
Ini bekerja di Bash dan Korn, juga dapat pergi dari tinggi ke rendah angka. Mungkin bukan yang tercepat atau tercantik tetapi bekerja cukup baik. Menangani negatif juga.
jika anda don't ingin menggunakan '
seq
' atau 'eval
' atauiota
atau aritmatika ekspansi format misalnya.untuk ((i=1;i<=AKHIR;i++))
, atau loop lain misalnya.sementara
, dan anda don't ingin 'printf
' dan 'echo
' hanya saja, maka ini solusi sederhana yang mungkin sesuai anggaran anda:a=1; b=5; d='for i in {'$a'..'$b'}; echo -n "$i"; dilakukan;' echo "$d" | bash
PS: Saya bash doesn't memiliki '
seq
' perintah pula.Diuji pada Mac OSX 10.6.8, Bash 3.2.48