Bagaimana cara saya mengulangi rentang angka yang ditentukan oleh variabel di Bash?

1545

Bagaimana cara saya mengulangi rentang angka di Bash ketika rentang diberikan oleh variabel?

Saya tahu saya bisa melakukan ini (disebut "ekspresi urutan" dalam dokumentasi Bash ):

 for i in {1..5}; do echo $i; done

Pemberian yang mana:

1
2
3
4
5

Namun, bagaimana saya bisa mengganti salah satu dari titik akhir rentang dengan variabel? Ini tidak berfungsi:

END=5
for i in {1..$END}; do echo $i; done

Yang mencetak:

{1..5}

eschercycle
sumber
26
Hai semua, informasi dan petunjuk yang saya baca di sini sangat membantu. Saya pikir yang terbaik adalah menghindari penggunaan seq. Alasannya adalah bahwa beberapa skrip harus portabel dan harus dijalankan pada berbagai sistem unix, di mana beberapa perintah mungkin tidak ada. Sekedar contoh, seq tidak ada secara default pada sistem FreeBSD.
9
Saya tidak ingat sejak versi Bash mana tepatnya, tetapi perintah ini mendukung nol tambahan juga. Yang terkadang sangat membantu. Perintah for i in {01..10}; do echo $i; doneakan memberikan angka seperti 01, 02, 03, ..., 10.
topr
1
Bagi mereka seperti saya yang hanya ingin beralih pada kisaran indeks array , cara bash akan menjadi: myarray=('a' 'b' 'c'); for i in ${!myarray[@]}; do echo $i; done(perhatikan tanda seru). Ini lebih spesifik daripada pertanyaan awal, tetapi bisa membantu. Lihat ekspansi parameter bash
PlasmaBinturong
1
Ekspansi Brace juga digunakan untuk ekspresi seperti {jpg,png,gif}yang tidak secara langsung dibahas di sini, meskipun jawabannya akan sama. Lihat ekspansi Brace dengan variabel? [duplikat] yang ditandai sebagai duplikat yang satu ini.
tripleee

Jawaban:

1747
for i in $(seq 1 $END); do echo $i; done

sunting: Saya lebih suka seqdaripada metode lain karena saya benar-benar dapat mengingatnya;)

Jiaaro
sumber
36
seq melibatkan eksekusi perintah eksternal yang biasanya memperlambat segalanya. Ini mungkin tidak masalah tetapi menjadi penting jika Anda menulis skrip untuk menangani banyak data.
paxdiablo
37
Baik untuk satu-liner. Solusi Pax juga baik-baik saja, tetapi jika kinerja benar-benar menjadi perhatian saya tidak akan menggunakan skrip shell.
eschercycle
17
seq dipanggil sekali saja untuk menghasilkan angka. exec () 'kan seharusnya tidak signifikan kecuali loop ini berada di dalam loop ketat lain.
Javier
29
Perintah eksternal tidak benar-benar relevan: jika Anda khawatir tentang overhead menjalankan perintah eksternal Anda tidak ingin menggunakan skrip shell sama sekali, tetapi umumnya pada unix overhead rendah. Namun, ada masalah penggunaan memori jika END tinggi.
Mark Baker
18
Perhatikan bahwa seq $ENDsudah cukup, karena standarnya mulai dari 1. Dari man seq: "Jika PERTAMA atau INCREMENT dihilangkan, standarnya adalah 1".
fedorqui 'SO berhenti merugikan'
475

The seqmetode adalah yang paling sederhana, tapi Bash telah built-in evaluasi aritmatika.

END=5
for ((i=1;i<=END;i++)); do
    echo $i
done
# ==> outputs 1 2 3 4 5 on separate lines

The for ((expr1;expr2;expr3));membangun bekerja seperti for (expr1;expr2;expr3)di C dan bahasa yang sama, dan seperti lainnya ((expr))kasus, Bash memperlakukan mereka sebagai aritmatika.

singkat
sumber
68
Cara ini menghindari overhead memori dari daftar besar, dan ketergantungan seq. Gunakan!
bobbogo
3
@MarinSagovac ini tidak bekerja dan tidak ada kesalahan sintaks. Apakah Anda yakin shell Anda adalah Bash?
gniourf_gniourf
3
@MarinSagovac Pastikan untuk membuat #!/bin/bashbaris pertama skrip Anda. wiki.ubuntu.com/…
Melebius
7
hanya pertanyaan yang sangat singkat tentang itu: mengapa ((i = 1; i <= END; i ++)) DAN TIDAK ((i = 1; i <= $ END; i ++)); kenapa tidak $ sebelum END?
Baedsch
5
@ Baedsch: untuk alasan yang sama saya tidak digunakan sebagai $ i. halaman buku bash menyatakan untuk evaluasi aritmatika: "Dalam sebuah ekspresi, variabel shell juga dapat dirujuk dengan nama tanpa menggunakan sintaksis ekspansi parameter."
user3188140
193

diskusi

Menggunakan seqtidak apa-apa, seperti yang disarankan Jiaaro. Pax Diablo menyarankan Bash loop untuk menghindari memanggil subproses, dengan keuntungan tambahan menjadi lebih ramah memori jika $ END terlalu besar. Zathrus melihat bug khas dalam implementasi loop, dan juga mengisyaratkan bahwa karena imerupakan variabel teks, konversi berkesinambungan ke-dan-nomor mondar-mandir dilakukan dengan perlambatan terkait.

aritmatika integer

Ini adalah versi perbaikan dari Bash loop:

typeset -i i END
let END=5 i=1
while ((i<=END)); do
    echo $i
    
    let i++
done

Jika satu-satunya hal yang kita inginkan adalah echo, maka kita bisa menulis echo $((i++)).

ephemient mengajari saya sesuatu: Bash memungkinkan for ((expr;expr;expr))konstruksi. Karena saya belum pernah membaca halaman manual untuk Bash (seperti yang saya lakukan dengan halaman manual shell Korn ( ksh), dan itu sudah lama sekali), saya melewatkan itu.

Begitu,

typeset -i i END # Let's be explicit
for ((i=1;i<=END;++i)); do echo $i; done

tampaknya menjadi cara yang paling hemat memori (tidak perlu mengalokasikan memori untuk menggunakan seqoutput, yang bisa menjadi masalah jika END sangat besar), walaupun mungkin bukan yang "tercepat".

pertanyaan awal

eschercycle mencatat bahwa notasi { a .. b } hanya bekerja dengan literal; benar, sesuai dengan manual Bash. Seseorang dapat mengatasi kendala ini dengan satu (internal) fork()tanpa exec()(seperti halnya dengan panggilan seq, yang menjadi gambar lain membutuhkan fork + exec):

for i in $(eval echo "{1..$END}"); do

Kedua eval dan echoBash builtins, tetapi fork()diperlukan untuk substitusi perintah ( $(…)konstruk).

tzot
sumber
1
Satu-satunya kelemahan ke loop gaya C yang tidak dapat menggunakan argumen baris perintah, karena mereka mulai dengan "$".
karatedog
3
@karatedog: for ((i=$1;i<=$2;++i)); do echo $i; donedalam skrip berfungsi dengan baik untuk saya di bash v.4.1.9, jadi saya tidak melihat masalah dengan argumen baris perintah. Apakah Anda bermaksud sesuatu yang lain?
tzot
Tampaknya solusi eval lebih cepat daripada yang dibangun dalam C-like untuk: $ time for ((i = 1; i <= 100000; ++ i)); lakukan:; dilakukan nyata 0m21.220s pengguna 0m19.763s sys 0m1.203s $ waktu untuk i in $ (eval echo "{1..100000}"); lakukan:; dilakukan; pengguna 0m13.881s nyata 0m13.536s sys 0m0.152s
Marcin Zaluski
3
Ya, tetapi eval itu jahat ... @MarcinZaluski time for i in $(seq 100000); do :; donejauh lebih cepat!
F. Hauri
Kinerja harus spesifik platform karena versi eval tercepat di mesin saya.
Andrew Prock
103

Inilah mengapa ekspresi asli tidak berfungsi.

Dari man bash :

Ekspansi brace dilakukan sebelum ekspansi lainnya, dan setiap karakter khusus ekspansi lainnya dipertahankan dalam hasilnya. Ini sangat tekstual. Bash tidak menerapkan interpretasi sintaksis apa pun untuk konteks ekspansi atau teks di antara kurung kurawal.

Jadi, ekspansi brace adalah sesuatu yang dilakukan sejak awal sebagai operasi makro murni tekstual, sebelum ekspansi parameter.

Shell adalah hibrida yang sangat dioptimalkan antara prosesor makro dan bahasa pemrograman yang lebih formal. Untuk mengoptimalkan kasus penggunaan umum, bahasa dibuat agak lebih kompleks dan beberapa batasan diterima.

Rekomendasi

Saya sarankan tetap menggunakan fitur Posix 1 . Ini berarti menggunakan for i in <list>; do, jika daftar sudah diketahui, jika tidak, gunakan whileatau seq, seperti dalam:

#!/bin/sh

limit=4

i=1; while [ $i -le $limit ]; do
  echo $i
  i=$(($i + 1))
done
# Or -----------------------
for i in $(seq 1 $limit); do
  echo $i
done


1. Bash adalah shell yang bagus dan saya menggunakannya secara interaktif, tetapi saya tidak memasukkan bash-isme ke dalam skrip saya. Script mungkin membutuhkan shell yang lebih cepat, yang lebih aman, yang lebih tertanam-gaya. Mereka mungkin perlu dijalankan pada apa pun yang diinstal sebagai / bin / sh, dan kemudian ada semua argumen pro-standar yang biasa. Ingat shellshock, alias bashdoor?

DigitalRoss
sumber
13
Saya tidak memiliki kekuatan, tetapi saya akan memindahkan ini sedikit ke atas daftar, di atas semua bash pusar-menatap tetapi segera setelah gaya-C untuk evaluasi loop dan aritmatika.
mateor
2
Implikasinya adalah bahwa ekspansi brace tidak menghemat banyak memori dibandingkan dengan sequntuk rentang besar. Misalnya, echo {1..1000000} | wcmengungkapkan bahwa gema menghasilkan 1 baris, sejuta kata, dan 6.888.896 byte. Mencoba seq 1 1000000 | wcmenghasilkan sejuta baris, sejuta kata, dan 6.888.896 byte dan juga lebih dari tujuh kali lebih cepat, sebagaimana diukur oleh timeperintah.
George
Catatan: Saya telah menyebutkan whilemetode POSIX sebelumnya dalam jawaban saya: stackoverflow.com/a/31365662/895245 Tapi senang Anda setuju :-)
Ciro Santilli 郝海东 冠状 病 六四 六四 事件 法轮功
Saya menyertakan jawaban ini dalam jawaban perbandingan kinerja di bawah. stackoverflow.com/a/54770805/117471 (Ini adalah catatan untuk saya sendiri untuk melacak mana yang tersisa untuk saya lakukan.)
Bruno Bronosky
@mateor Saya pikir gaya-C untuk evaluasi loop dan aritmatika adalah solusi yang sama. Apakah saya melewatkan sesuatu?
Oscar Zhang
73

Cara POSIX

Jika Anda peduli tentang portabilitas, gunakan contoh dari standar POSIX :

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

Keluaran:

2
3
4
5

Hal-hal yang bukan POSIX:

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
sumber
Baru saja mendapatkan 4 suara positif pada jawaban ini, yang sangat tidak biasa. Jika ini diposting di beberapa situs web agregasi tautan, tolong beri saya tautan, tepuk tangan.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Kutipan mengacu pada x, bukan keseluruhan ekspresi. $((x + 1))baik-baik saja.
chepner
Meskipun tidak portabel, dan berbeda dari GNU seq(BSD seqmemungkinkan Anda untuk mengatur urutan string pemutusan dengan -t), FreeBSD dan NetBSD juga memiliki seqmasing-masing sejak 9.0 dan 3.0.
Adrian Günter
@CiroSantilli @chepner $((x+1))dan $((x + 1))parse persis sama, seperti ketika parser tokenizes x+1akan dibagi menjadi 3 token: x, +, dan 1. xbukan token numerik yang valid, tetapi token nama variabel yang valid, namun x+tidak, karenanya pemisahan. +adalah token operator aritmatika yang valid, namun +1tidak, sehingga token tersebut dibagi lagi di sana. Dan seterusnya.
Adrian Günter
Saya menyertakan jawaban ini dalam jawaban perbandingan kinerja di bawah. stackoverflow.com/a/54770805/117471 (Ini adalah catatan untuk saya sendiri untuk melacak mana yang tersisa untuk saya lakukan.)
Bruno Bronosky
35

Lapisan tipuan lainnya:

for i in $(eval echo {1..$END}); do
    
bobbogo
sumber
2
+1: Juga, eval 'for i in {1 ..' $ END '}; do ... 'tampaknya merupakan cara alami untuk menyelesaikan masalah ini.
William Pursell
28

Kamu bisa menggunakan

for i in $(seq $END); do echo $i; done
Peter Hoffmann
sumber
seq melibatkan eksekusi perintah eksternal yang biasanya memperlambat segalanya.
paxdiablo
9
Itu tidak melibatkan pelaksanaan perintah eksternal untuk setiap iterasi, hanya sekali. Jika waktu untuk meluncurkan satu perintah eksternal adalah masalah, Anda menggunakan bahasa yang salah.
Mark Baker
1
Jadi apakah bersarang satu-satunya kasus di mana ini penting? Saya bertanya-tanya apakah ada perbedaan kinerja, atau efek samping teknis yang tidak diketahui?
Sqeaky
@Queaky Itu pertanyaan terpisah yang dijawab di sini: stackoverflow.com/questions/4708549/…
tripleee
Saya menyertakan jawaban ini dalam jawaban perbandingan kinerja di bawah. stackoverflow.com/a/54770805/117471 (Ini adalah catatan untuk saya sendiri untuk melacak mana yang tersisa untuk saya lakukan.)
Bruno Bronosky
21

Jika Anda membutuhkannya awalan daripada yang Anda mungkin suka ini

 for ((i=7;i<=12;i++)); do echo `printf "%2.0d\n" $i |sed "s/ /0/"`;done

itu akan menghasilkan

07
08
09
10
11
12
Hossbear
sumber
4
Bukankah printf "%02d\n" $ilebih mudah dari itu printf "%2.0d\n" $i |sed "s/ /0/"?
zb226
19

Jika Anda menggunakan BSD / OS X Anda dapat menggunakan jot alih-alih seq:

for i in $(jot $END); do echo $i; done
warga negara
sumber
17

Ini berfungsi dengan baik di bash:

END=5
i=1 ; while [[ $i -le $END ]] ; do
    echo $i
    ((i = i + 1))
done
paxdiablo
sumber
6
echo $((i++))bekerja dan menggabungkannya menjadi satu baris.
Bruno Bronosky
1
Ini memiliki ekstensi bash yang tidak dibutuhkan. Versi POSIX: stackoverflow.com/a/31365662/895245
Ciro Santilli 郝海东 冠状 病 六四 事件 事件
1
@ Ciro, karena pertanyaannya secara spesifik menyatakan bash, dan memiliki tag bash, saya pikir Anda mungkin akan menemukan bahwa bash 'ekstensi' lebih dari oke :-)
paxdiablo
@paxdiablo Saya tidak bermaksud itu tidak benar, tapi mengapa tidak portabel ketika kita bisa ;-)
Ciro Santilli 郝海东 冠状 病 六四 六四 事件
Dalam bash, kita bisa melakukan while [[ i++ -le "$END" ]]; dountuk melakukan (post-) selisih dalam tes
Aaron McDaid
14

Saya telah menggabungkan beberapa ide di sini dan mengukur kinerja.

TL; DR Takeaways:

  1. seqdan {..}sangat cepat
  2. fordan whileloop lambat
  3. $( ) lambat
  4. for (( ; ; )) loop lebih lambat
  5. $(( )) bahkan lebih lambat
  6. Khawatir tentang nomor N di memori (seq atau {..}) konyol (setidaknya hingga 1 juta.)

Ini bukan kesimpulan . Anda harus melihat kode C di belakang masing-masing untuk menarik kesimpulan. Ini lebih tentang bagaimana kita cenderung menggunakan masing-masing mekanisme ini untuk pengulangan kode. Kebanyakan operasi tunggal cukup dekat untuk menjadi kecepatan yang sama sehingga tidak menjadi masalah dalam banyak kasus. Tetapi mekanisme seperti for (( i=1; i<=1000000; i++ ))ini adalah banyak operasi yang dapat Anda lihat secara visual. Ini juga lebih banyak operasi per loop dari yang Anda dapatkanfor i in $(seq 1 1000000) . Dan itu mungkin tidak jelas bagi Anda, itulah sebabnya melakukan tes seperti ini sangat berharga.

Demo

# show that seq is fast
$ time (seq 1 1000000 | wc)
 1000000 1000000 6888894

real    0m0.227s
user    0m0.239s
sys     0m0.008s

# show that {..} is fast
$ time (echo {1..1000000} | wc)
       1 1000000 6888896

real    0m1.778s
user    0m1.735s
sys     0m0.072s

# Show that for loops (even with a : noop) are slow
$ time (for i in {1..1000000} ; do :; done | wc)
       0       0       0

real    0m3.642s
user    0m3.582s
sys 0m0.057s

# show that echo is slow
$ time (for i in {1..1000000} ; do echo $i; done | wc)
 1000000 1000000 6888896

real    0m7.480s
user    0m6.803s
sys     0m2.580s

$ time (for i in $(seq 1 1000000) ; do echo $i; done | wc)
 1000000 1000000 6888894

real    0m7.029s
user    0m6.335s
sys     0m2.666s

# show that C-style for loops are slower
$ time (for (( i=1; i<=1000000; i++ )) ; do echo $i; done | wc)
 1000000 1000000 6888896

real    0m12.391s
user    0m11.069s
sys     0m3.437s

# show that arithmetic expansion is even slower
$ time (i=1; e=1000000; while [ $i -le $e ]; do echo $i; i=$(($i+1)); done | wc)
 1000000 1000000 6888896

real    0m19.696s
user    0m18.017s
sys     0m3.806s

$ time (i=1; e=1000000; while [ $i -le $e ]; do echo $i; ((i=i+1)); done | wc)
 1000000 1000000 6888896

real    0m18.629s
user    0m16.843s
sys     0m3.936s

$ time (i=1; e=1000000; while [ $i -le $e ]; do echo $((i++)); done | wc)
 1000000 1000000 6888896

real    0m17.012s
user    0m15.319s
sys     0m3.906s

# even a noop is slow
$ time (i=1; e=1000000; while [ $((i++)) -le $e ]; do :; done | wc)
       0       0       0

real    0m12.679s
user    0m11.658s
sys 0m1.004s
Bruno Bronosky
sumber
1
Bagus! Namun, jangan setuju dengan ringkasan Anda. Sepertinya saya $(seq)adalah tentang kecepatan yang sama dengan {a..b}. Juga, setiap operasi membutuhkan waktu yang sama, jadi menambahkan sekitar 4μs untuk setiap iterasi dari loop untuk saya. Di sini operasi adalah gema dalam tubuh, perbandingan aritmatika, kenaikan, dll. Apakah ini mengejutkan? Siapa yang peduli berapa lama perangkat loop untuk melakukan tugasnya — runtime cenderung didominasi oleh konten loop.
bobbogo
@ Bobobogo Anda benar, ini benar-benar tentang hitungan operasi. Saya memperbarui jawaban saya untuk mencerminkan ini. Banyak panggilan yang kami lakukan benar-benar melakukan operasi lebih dari yang kami harapkan. Saya mempersempit ini dari daftar sekitar 50 tes yang saya jalankan. Saya berharap bahwa penelitian saya terlalu kutu buku bahkan untuk kerumunan ini. Seperti biasa, saya sarankan memprioritaskan upaya pengkodean Anda seperti: Buat lebih pendek; Buat itu mudah dibaca; Buat lebih cepat; Jadikan portable. Seringkali # 1 menyebabkan # 3. Jangan buang waktu Anda di # 4 sampai Anda harus melakukannya.
Bruno Bronosky
8

Saya tahu pertanyaan ini tentang bash, tetapi - hanya untuk catatan - ksh93lebih pintar dan mengimplementasikannya seperti yang diharapkan:

$ ksh -c 'i=5; for x in {1..$i}; do echo "$x"; done'
1
2
3
4
5
$ ksh -c 'echo $KSH_VERSION'
Version JM 93u+ 2012-02-29

$ bash -c 'i=5; for x in {1..$i}; do echo "$x"; done'
{1..5}
Adrian Frühwirth
sumber
8

Ini adalah cara lain:

end=5
for i in $(bash -c "echo {1..${end}}"); do echo $i; done
Jahid
sumber
1
Ini memiliki overhead pemijahan shell lain.
codeforester
1
Sebenarnya, ini sangat mengerikan karena memunculkan 2 cangkang ketika saya sudah cukup.
Bruno Bronosky
8

Jika Anda ingin tetap sedekat mungkin dengan sintaks ekspresi-brace, cobalah rangefungsi dari bash-tricks 'range.bash .

Misalnya, semua hal berikut akan melakukan hal yang sama persis seperti echo {1..10}:

source range.bash
one=1
ten=10

range {$one..$ten}
range $one $ten
range {1..$ten}
range {1..10}

Ia mencoba untuk mendukung sintaks bash asli dengan sesedikit mungkin "gotcha": tidak hanya variabel yang didukung, tetapi perilaku rentang tidak valid yang sering diberikan sebagai string (misalnya for i in {1..a}; do echo $i; done) dicegah juga.

Jawaban lain akan berfungsi dalam kebanyakan kasus, tetapi mereka semua memiliki setidaknya satu dari kekurangan berikut:

  • Banyak dari mereka menggunakan subkulit , yang dapat merusak kinerja dan mungkin tidak dapat dilakukan pada beberapa sistem.
  • Banyak dari mereka mengandalkan program eksternal. Bahkan seqadalah biner yang harus diinstal untuk digunakan, harus dimuat oleh bash, dan harus berisi program yang Anda harapkan, agar bisa berfungsi dalam kasus ini. Di mana-mana atau tidak, itu jauh lebih bisa diandalkan daripada hanya bahasa Bash itu sendiri.
  • Solusi yang hanya menggunakan fungsionalitas Bash asli, seperti @ ephemient, tidak akan berfungsi pada rentang alfabet, seperti {a..z}; ekspansi brace akan. Pertanyaannya adalah tentang rentang angka , jadi ini adalah pertengkaran.
  • Kebanyakan dari mereka tidak secara visual mirip dengan {1..10}sintaks rentang yang dikembangkan, jadi program yang menggunakan keduanya mungkin sedikit lebih sulit untuk dibaca.
  • @ bobbogo jawaban menggunakan beberapa sintaks akrab, tetapi melakukan sesuatu yang tidak terduga jika $ENDvariabel bukan rentang "bookend" yang valid untuk sisi lain dari rentang. Jika END=a, misalnya, kesalahan tidak akan terjadi dan nilai kata demi kata {1..a}akan digaungkan. Ini adalah perilaku default Bash, juga - itu sering tak terduga.

Penafian: Saya adalah pembuat kode tertaut.

Zac B
sumber
7

Ganti {}dengan (( )):

tmpstart=0;
tmpend=4;

for (( i=$tmpstart; i<=$tmpend; i++ )) ; do 
echo $i ;
done

Hasil:

0
1
2
3
4
BashTheKeyboard
sumber
Saya menyertakan jawaban ini dalam jawaban perbandingan kinerja di bawah. stackoverflow.com/a/54770805/117471 (Ini adalah catatan untuk saya sendiri untuk melacak mana yang tersisa untuk saya lakukan.)
Bruno Bronosky
6

Ini semua bagus tapi seq seharusnya sudah usang dan sebagian besar hanya berfungsi dengan rentang numerik.

Jika Anda menyertakan for loop dalam tanda kutip ganda, variabel awal dan akhir akan ditinjau ketika Anda mengulangi string, dan Anda dapat mengirimkan string kembali ke BASH untuk dieksekusi. $iperlu melarikan diri dengan sehingga TIDAK dievaluasi sebelum dikirim ke subkulit.

RANGE_START=a
RANGE_END=z
echo -e "for i in {$RANGE_START..$RANGE_END}; do echo \\${i}; done" | bash

Output ini juga dapat ditugaskan ke variabel:

VAR=`echo -e "for i in {$RANGE_START..$RANGE_END}; do echo \\${i}; done" | bash`

Satu-satunya "overhead" yang harus dihasilkan ini adalah instance kedua dari bash sehingga harus sesuai untuk operasi intensif.

SuperBob
sumber
5

Jika Anda melakukan perintah shell dan Anda (seperti saya) memiliki jimat untuk pipelining, ini bagus:

seq 1 $END | xargs -I {} echo {}

Alex Spangher
sumber
3

Ada banyak cara untuk melakukan ini, namun yang saya sukai diberikan di bawah ini

Menggunakan seq

Sinopsis dari man seq

$ seq [-w] [-f format] [-s string] [-t string] [first [incr]] last

Sintaksis

Perintah penuh
seq first incr last

  • pertama adalah nomor awal dalam urutan [adalah opsional, secara default: 1]
  • increment [kenaikan adalah opsional, secara default: 1]
  • terakhir adalah angka terakhir dalam urutan

Contoh:

$ seq 1 2 10
1 3 5 7 9

Hanya dengan yang pertama dan terakhir:

$ seq 1 5
1 2 3 4 5

Hanya dengan yang terakhir:

$ seq 5
1 2 3 4 5

Menggunakan {first..last..incr}

Di sini pertama dan terakhir adalah wajib dan tambahan adalah opsional

Menggunakan hanya pertama dan terakhir

$ echo {1..5}
1 2 3 4 5

Menggunakan incr

$ echo {1..10..2}
1 3 5 7 9

Anda dapat menggunakan ini bahkan untuk karakter seperti di bawah ini

$ echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z
theBuzzyCoder
sumber
3

jika Anda tidak ingin menggunakan format ekspansi ' seq' atau ' eval' atau jotatau aritmatika misalnya. for ((i=1;i<=END;i++)), atau loop lain misalnya. while, dan Anda tidak ingin ' printf' dan senang ' echo' saja, maka solusi sederhana ini mungkin sesuai dengan anggaran Anda:

a=1; b=5; d='for i in {'$a'..'$b'}; do echo -n "$i"; done;' echo "$d" | bash

PS: Lagipula bash saya tidak punya seqperintah.

Diuji pada Mac OSX 10.6.8, Bash 3.2.48

Zimba
sumber
0

Ini bekerja di Bash dan Korn, juga bisa berubah dari yang lebih tinggi ke angka yang lebih rendah. Mungkin tidak tercepat atau tercantik tetapi bekerja dengan cukup baik. Menangani negatif juga.

function num_range {
   # Return a range of whole numbers from beginning value to ending value.
   # >>> num_range start end
   # start: Whole number to start with.
   # end: Whole number to end with.
   typeset s e v
   s=${1}
   e=${2}
   if (( ${e} >= ${s} )); then
      v=${s}
      while (( ${v} <= ${e} )); do
         echo ${v}
         ((v=v+1))
      done
   elif (( ${e} < ${s} )); then
      v=${s}
      while (( ${v} >= ${e} )); do
         echo ${v}
         ((v=v-1))
      done
   fi
}

function test_num_range {
   num_range 1 3 | egrep "1|2|3" | assert_lc 3
   num_range 1 3 | head -1 | assert_eq 1
   num_range -1 1 | head -1 | assert_eq "-1"
   num_range 3 1 | egrep "1|2|3" | assert_lc 3
   num_range 3 1 | head -1 | assert_eq 3
   num_range 1 -1 | tail -1 | assert_eq "-1"
}
Ethan Post
sumber