Bagaimana saya bisa membuat loop aritmatika dalam skrip POSIX shell?

12

Saya tahu cara membuat forloop aritmatika di bash.

Bagaimana seseorang dapat melakukan loop yang setara dalam skrip POSIX shell?

Karena ada berbagai cara untuk mencapai tujuan yang sama, jangan ragu untuk menambahkan jawaban Anda sendiri dan menguraikan sedikit tentang cara kerjanya.

Contoh satu bashloop seperti berikut:

#!/bin/bash
for (( i=1; i != 10; i++ ))
do
    echo "$i"
done
LinuxSecurityFreak
sumber
@ StéphaneChazelas karena itu adalah catatan tentang sejarah yang tampaknya didasarkan pada kesalahpahaman karena OP tidak menyarankan bahwa itu adalah hal bash, tetapi hanya menggunakan bash sebagai contoh. Tampaknya tidak relevan.
terdon

Jawaban:

13

Saya telah menemukan informasi yang berguna di wiki Shellcheck.net , saya kutip:

  1. Pesta:

    for ((init; test; next)); do foo; done
  2. POSIX:

    : "$((init))"
    while [ "$((test))" -ne 0 ]; do foo; : "$((next))"; done
    

meskipun waspadalah yang i++bukan POSIX jadi harus diterjemahkan, misalnya ke i += 1atau i = i + 1.


Jadi skrip di atas dalam pertanyaan dapat ditulis ulang dengan POSIX menggunakan aturan-aturan seperti ini:

#!/bin/sh
: "$((i=1))"
while [ "$((i != 0))" -ne 0 ]
do
    echo "$i"
    : "$((i = i + 1))"
done

Meskipun di sini, Anda dapat membuatnya lebih terbaca dengan:

#!/bin/sh
i=1
while [ "$i" -ne 10 ]
do
    echo "$i"
    i=$((i + 1))
done

seperti pada init, kami memberikan nilai konstan, jadi kami tidak perlu mengevaluasi ekspresi aritmatika. The i != 10di testdapat dengan mudah diterjemahkan ke [ekspresi, dan untuk next, menggunakan shell tugas variabel sebagai lawan tugas variabel dalam sebuah ekspresi aritmatika, mari kita menyingkirkan :dan kebutuhan untuk mengutip.


Di samping i++-> i = i + 1, ada lebih banyak terjemahan dari konstruksi spesifik ksh / bash yang bukan POSIX yang mungkin harus Anda lakukan:

  • i=1, j=2. The ,Operator aritmatika tidak benar-benar POSIX (dan konflik dengan pemisah desimal di beberapa lokal dengan ksh93). Anda bisa menggantinya dengan operator lain seperti +di : "$(((i=1) + (j=2)))"tetapi menggunakan i=1 j=2akan jauh lebih terbaca.
  • a[0]=1: tidak ada array di kulit POSIX
  • i = 2**20: tidak ada operator daya dalam sintaks POSIX shell. <<didukung meskipun begitu untuk kekuatan dua, satu dapat digunakan i = 1 << 20. Untuk kekuatan lain, seseorang dapat menggunakan bc:i=$(echo "3 ^ 20" | bc)
  • i = RANDOM % 3: bukan POSIX. Yang paling dekat di toolchest POSIX adalah i=$(awk 'BEGIN{srand(); print int(rand() * 3)}').
LinuxSecurityFreak
sumber
2

terima kasih atas pengetahuan latar belakang mendalam tentang perbedaan. Setetes pengganti yang berfungsi untuk saya saat menggunakan shellcheck.net adalah seperti di bawah ini.

PESTA

for i in {1..100}; do  
  ...  
done  

POSIX

i=0; while [ $i -le 100 ]; do  
  ...  
  i=$(( i + 1 ))  
done

beberapa orang mencatat bahwa seq juga merupakan pilihan menggunakan seq 1 10. Membuat loop, namun ini tergantung bahwa os memiliki seq.

Jimmy MG Lim
sumber
perhatikan bahwa saya telah menempatkan i = 0 pada baris yang sama dengan while. meskipun keterbacaannya tidak bagus, itu adalah drop-in satu liner. Ini memastikan bahwa variabel i tidak ternodai oleh tempat lain yang didefinisikan dalam skrip.
Jimmy MG Lim