09 novembre, 2019

Bash scripts - cose che devo ricordarmi - 3

Alex Barucchieri

Continuo da qui con operazioni aritmetiche in Bash. In particolare la divisione, è trattata un po' troppo semplicemente, esempio (ediv):

N=$1
M=$2
echo $N '/' $M '=' $(( N / M ))


$ bash ediv 3 3
3 / 3 = 1

$
bash ediv 4 3
4 / 3 = 1

$
bash ediv 5 3
5 / 3 = 1

$
bash ediv 6 3
6 / 3 = 2

$

come si vede il risultato è sempre la parte intera, scartando il resto (modulo); in genere questa operazione è conosciuta come floor ma ci sono anche il round (arrotondamento) e il ceil, intero in eccesso.

Conviene, visto che ci sono ben 3 casi simili creare una libreria (parola grossa, e poi a rigore library si dovrebbe tradurre con biblioteca, ma è così da 70 anni) con le tre funzioni (def_div):

function floor() {
    echo $(( $1 / $2 ))
}

function round() {
    local Iden
    Iden=$(( $2 / 2 ))
    echo $(( $(( $1 + $Iden )) / $2 ))
}

function ceil() {
    local R S
    R=$(( $1 % $2 ))
    S=$(( $1 / $2 ))
    (( S = $R==0 ? S : S + 1 ))
    echo $S
}


Per usarle basta richiamare la libreria (cioè questo file) nello script, esempio (f-div):

N=$1
M=$2
. def_div  # carica la libreria con le funzioni
echo 'floor:' $(floor $N $M)
echo 'round:' $(round $N $M)
echo 'ceil :' $(ceil $N $M)


ed ecco:

$ bash f-div 9 3
floor: 3
round: 3
ceil : 3

$
bash f-div 10 3
floor: 3
round: 3
ceil : 4

$
bash f-div 11 3
floor: 3
round: 4
ceil : 4

$
bash f-div 12 3
floor: 4
round: 4
ceil : 4

$


Volendo si può fare anche meglio: calcolare il valore reale della divisione in Bash! L'hanno proposto in tanti, su Stack Overflow e altrove (chi ha copiato chi?), ecco div, file def_r_div:

function div {
  local _d=${3:-2}
  local _n=0000000000
  _n=${_n:0:$_d}
  local _r=$(($1$_n/$2))
  _r=${_r:0:-$_d}.${_r: -$_d}
  echo $_r
}


Il test (file t_div):

. def_r_div  # carica la funzione div
echo "$1 / $2 =" $(div $*)


ottengo:

$ bash t_div 9 3
9 / 3 = 3.00

$
bash t_div 9 3 4
9 / 3 = 3.0000

$
bash t_div 10 3
10 / 3 = 3.33

$
bash t_div 11 3
11 / 3 = 3.66

$
bash t_div 12 3
12 / 3 = 4.00

$


sì, niente arrotondamento decimale, ma c'è anche un bug:

$ bash t_div 12 3 0
12 / 3 = .4

$


errore! la correzione non è difficile ma allunga il codice della funzione; lasciata come esercizio.

Piuttosto se si dovesse usare per più casi si può caricare la funzione in memoria, cosi:

$ source def_r_div

$
div 9 3
3.00

$
div 10 3
3.33

$
div 11 3 5
3.66666

$
div 12 3
4.00

$


La funzione resta in memoria nel terminale finché questo non viene chiuso. Per i vecchi come me questa cosa è (almeno un po') stupefacente: allora si sarebbe aperta una sub-shell all'interno della quale svolgere le elaborazioni e infine chiusa con ctrl-D (o exit), così:

$ div 12 3

Comando «div» non trovato, si intendeva forse: [...]
$ bash
$
source def_r_div

$
div 12 3
4.00

$
exit
exit

$
div 12 3

Comando «div» non trovato, si intendeva forse: ...


$

 
E sì, non sembra ma sono passati 30 anni (circa, troppi, non so contarli).

Ah! 'ncora una cosa: lo sappiamo tutti che l'aritmetica si può fare in molti altri modi, sovente più semplici, p.es. con Python, JavaScript o ______________. Ma anche (io farei così):

$ echo "scale=2; 22 / 7" | bc
3.14

$
echo "scale=4; 22 / 7" | bc
3.1428

$


🔴

Nessun commento:

Posta un commento