Bagaimana saya bisa membuat skrip untuk dihitung oleh balita?

10

Saya mencoba membuat skrip bash yang sangat sederhana untuk mendaftar semua kelipatan lima antara 375 dan 3500 (375, 380, 385 ...). Satu hal yang saya coba dan tidak berhasil adalah:

for i in {375..3500}
do
        echo $i
        (($i += 5))
done

Saya menyerah setelah beberapa saat dan menulis ini dalam BASIC dalam waktu sekitar 15 detik:

10 count = 375
20 print count
30 count = count+5
40 if count < 3500 then goto 20

Bagaimana saya bisa membuat program BASIC saya dalam skrip bash?

aswine
sumber
ini lebih baik pada stack overflow - Unix & Linux adalah tentang OS, bukan coding. Pengodean untuk stackoverflow.
noɥʇʎԀʎzɐɹƆ

Jawaban:

20

Sebagai alternatif, gaya C tradisional untuk loop dapat digunakan:

for ((i=375; i<=3500; i+=5)); do
    echo $i
done

Ini mungkin kurang jelas daripada menggunakan seq, tetapi tidak menelurkan subproses. Meskipun karena saya terbiasa dengan C, saya tidak akan kesulitan memahami ini, tapi YMMV.

Muzer
sumber
1
Kelemahan dari metode ini adalah bahwa ini kurang portabel. Metode seq bekerja di POSIX sh.
Wouter Verhelst
Oh, apakah C-style untuk loop tidak ada di POSIX sh? Anda belajar sesuatu yang baru setiap hari ...
Muzer
2
@WouterVerhelst Yah, kecuali jika Anda terbatas pada POSIX sh, Anda tidak akan memiliki seq, yang merupakan bagian dari GNU coreutils. busybox memiliki implementasi yang lebih terbatas, tetapi di luar itu, banyak sistem embedded kemungkinan tidak akan memilikinya sama sekali.
Chris Down
1
@Muzer - setidaknya dashtidak mendukungnya. @ ChrisDown - Tidak ada alasan mengapa "POSIX sh" harus menyiratkan "no seq". Tapi ya, memang, saya telah mengabaikan fakta bahwa seq tidak ditentukan oleh POSIX.
Wouter Verhelst
27

Karena Anda tetap menggunakan ekspansi brace, jadi manfaatkan fiturnya sepenuhnya:

echo {375..3500..5}

Anda juga dapat menggunakan teknik ini untuk mencetak setiap angka dalam baris terpisah dengan teks opsional dengan menggunakan printfalih-alih echo, misalnya:

$ printf "Number %s is generated.\n" {375..3500..5}
Number 375 is generated.
Number 380 is generated.
Number 385 is generated.
...

Edit

Seperti yang ditunjukkan oleh @kojiro dalam komentar Mac OS menggunakan bash 3 sebagai shell default, yang tidak mendukung peningkatan dalam ekspresi urutan ekspansi brace. Anda perlu memutakhirkan ke bash versi 4 atau menggunakan shell lain yang mendukungnya (mis. Zsh terbaru).

jimmij
sumber
2
Ini tidak berfungsi untuk saya di Mac OS 10.10.3. Ini menghasilkan '{375..3500..5}'.
aswine
6
@karena itu sebabnya Anda harus selalu menyebutkan OS Anda saat bertanya. Terutama karena OSX memiliki sejumlah perbedaan baik dari UNICE lain dan dari Linux.
terdon
2
Ini bukan perbedaan OS per se. Ini perbedaan versi. OS X hanya memiliki BASH 3 OOTB. Anda dapat menginstal BASH dari abad ini menggunakan hampir semua manajer paket OS X. Saya menggunakan Home Brew, untuk satu.
kojiro
2
Fakta bahwa ekspansi gagal dijalankan secara langsung tetapi berhasil dalam bash -ccukup banyak jaminan bahwa dua shell berbeda terlibat. Apakah $SHELL --versionmengatakan itu bash 3?
Dhag
3
@ mikeserv Jawabannya sangat sederhana - saya tidak menulis itu karena saya tidak tahu di mana versi bashfitur ini diperkenalkan. Saya sudah mempelajarinya sendiri dari komentar kojiro. Dan tolong jangan membandingkan saya dengan SC, saya benar-benar tidak tahu semua detail dan sejarah semua cangkang sejak akhir 1970. Sekali lagi - Jika Anda mengharapkan itu, harap harap downvote.
jimmij
17

Menggunakan SEQ (1)

for i in $(seq 375 5 3500)
do
    echo $i
done

Atau, cukup:

seq 375 5 3500
Emerik
sumber
4
Bahkan lebih sederhana adalah dengan menjatuhkan loop dan hanya menggunakan seq 375 5 3500.
Dhag
11

Cuplikan for-loop Anda tidak berfungsi seperti yang Anda minta karena dua alasan:

  • (($i += 5))- di sini $idiperluas ke nilai i. Dengan demikian ekspansi akan menjadi sesuatu seperti ((375 += 5)), yang tidak masuk akal (mencoba untuk menetapkan angka literal ke angka literal lain). Ini biasanya akan dicapai dengan ((i += 5))(tidak $untuk memperluas variabel)
  • The {375..3500}akan diperluas sebelum iterasi pertama dari loop. Ini akan menjadi daftar angka 375 376 ... 3499 3500. Untuk setiap iterasi dari loop, iakan ditugaskan ke masing-masing angka ini, satu-per-satu. Jadi pada awal setiap iterasi, iakan dipindahkan ke nilai berikutnya dalam daftar itu, menghitung dalam langkah-langkah 1. Secara ((i += 5))efektif tidak melakukan apa-apa - ia menambahkan 5 ke i, tetapi kemudian saya baru saja ditugaskan lagi pada awal iterasi berikutnya.

Saya pikir saya paling suka for (( ; ; ))jawabannya, tetapi berikut adalah beberapa alternatif untuk membuat Anda berpikir:


Karena kita berurusan dengan kelipatan 5, dan {a..b..i}ekspansi tidak didukung di bash versi 3.2.57 (1) (di OS X), maka kita dapat melakukan hal yang agak misterius ini sebagai gantinya:

for i in 375 {38..349}{0,5} 3500; do
    echo $i
done

Ini menunjukkan bagaimana bash dapat digunakan untuk membuat produk kartesius.


Saya pikir secara umum for for adalah cara yang paling nyaman untuk melakukan ini, tetapi jika Anda tertarik, Anda dapat menggunakan program while-loop (sedikit lebih dekat dengan BASIC Anda):

count=375
while (( count <= 3500 )); do
    echo $count
    (( count += 5 ))
done
Trauma Digital
sumber
1
@jimmij ... apa itu produk Cartesian? Anda dapat menurunkan komentar jika Anda mau, dan saya bahkan tidak akan peduli jika Anda memberi tahu saya. Saya penasaran.
mikeserv
1
@mikeserv Anda tidak dapat menurunkan komentar: en.wikipedia.org/wiki/Cartesian_product
jimmij
@jimmij - masih oke dengan saya jika Anda melakukannya.
mikeserv
@jimmij Iblis yang kamu bicarakan? Produk Cartesian adalah satu set tupel dari setiap kombinasi elemen yang mungkin dari dua set (yang mungkin atau tidak mungkin set yang sama). Lebih tepatnya, itu hanya "semua kombinasi yang mungkin" dari dua kelompok hal. Saya hanya melihat satu set. Bagaimana ada produk Cartesian di sini?
jpmc26
1
@ jpmc26 Anda benar, ini "semua kombinasi yang mungkin", jadi mari kita buat plot dua sumbu - pertama tulis semua angka dari 38hingga 349. Yang kedua hanya dua angka: 0dan 5. Sekarang mari kita ciptakan pasang memerintahkan mereka semua kombinasi - Anda dapat menulis mereka sebagai set: {38,0}, {38,5}... {349,5}, atau hanya menghapus simbol berlebihan {, ,dan }dan ... nomor baru 380... 3495.
jimmij
5

Meskipun ada, tentu saja, aplikasi untuk itu ( seq 375 5 3500), ada berbagai cara untuk melakukan ini dari commandline. Meskipun yang tercepat dan paling sederhana hanya menggunakan seq, berikut adalah beberapa opsi lain:

for i in {375..3500}; do [[ (($i % 5)) -eq 0 ]] && echo $i; done

i=370; while [ $i -le 3500 ]; do printf "%s\n" $((i+=5)); done

perl -le '$i=shift;while($i<=$ARGV[0]){print $i; $i+=5; }' 375 3500
perl -le 'map{print $_ + 5} 370..3495'

awk 'BEGIN{ for(i=375;i<=3500;i+=5){print i}}'
terdon
sumber
Nit pick: $(($i % 5))dapat ditulis $((i % 5)).
G-Man Mengatakan 'Reinstate Monica'
4

POSIXly:

i=370
while [ 3500 -gt "$i" ]
do    echo "$((i+=5))"
done

...atau...

echo 'for(x=370;x<=3500;x+=5)x' |bc

Saya tidak tahu mengapa Anda melakukannya dengan cara lain. Kecuali, tentu saja ...

seq 375 5 3500

... atau dengan dc:

echo '370[5+pd3500>p]splpx'|dc
mikeserv
sumber
Saya ingin tahu bagaimana dckode ini bekerja. Ini tentu lebih menarik daripada menggunakan seq.
Dhag
1
@ Dhag - itu menulis lingkaran kecil. itu saves [string ]ke atas parray, kemudian lmembawanya kembali ke atas tumpukan dan xmembuatnya sebagai makro. Dalam makro kami mendorong 5pada tumpukan kemudian pop itu dan yang kedua dari atas dan +mereka, mengganti kedua nilai stack dengan jumlah mereka, yang dilubangi pdan di- dupgrade sebelum 3500didorong pada stack, ketika kita pop dan dupe yang baru saja kita buat untuk perbandingan >. Jika 3500lebih besar, kami memuat dan mengeksekusi sebagai makro string yang disimpan dalam parray (makro kami saat ini) , atau jika tidak rusak.
mikeserv
3

Jika Anda terjebak di Bash 3:

echo {375..3500} | tr ' ' '\n' | sed -n 'p;n;n;n;n'

dan jika Anda lebih suka awk:

echo {375..3500} | tr ' ' '\n' | awk '!((NR-1)%5)'

Saya tidak tahu tentang ekspansi brace - itu benar-benar keren.

bonh
sumber