Come posso aggiungere numeri in uno script Bash?


567

Ho questo script Bash e ho avuto un problema nella riga 16. Come posso prendere il risultato precedente della riga 15 e aggiungerlo alla variabile nella riga 16?

#!/bin/bash

num=0
metab=0

for ((i=1; i<=2; i++)); do
    for j in `ls output-$i-*`; do
        echo "$j"

        metab=$(cat $j|grep EndBuffer|awk '{sum+=$2} END { print sum/120}') (line15)
        num= $num + $metab   (line16)
    done
    echo "$num"
 done

2
Penso che stamperai un valore zero anche usando la seguente risposta. C'è un trucco, ad esempio una sottostazione di processo, per portare il valore finale di "inside num" a "outside num".
— Scott Chu,

Risposte:


977

Per numeri interi :

  • Usa espansione aritmetica :$((EXPR))

    num=$((num1 + num2))
    num=$(($num1 + $num2))       # Also works
    num=$((num1 + 2 + 3))        # ...
    num=$[num1+num2]             # Old, deprecated arithmetic expression syntax
  • Utilizzo exprdell'utilità esterna . Si noti che questo è necessario solo per sistemi molto vecchi.

    num=`expr $num1 + $num2`     # Whitespace for expr is important

Per virgola mobile :

Bash non supporta direttamente questo, ma ci sono un paio di strumenti esterni che puoi usare:

num=$(awk "BEGIN {print $num1+$num2; exit}")
num=$(python -c "print $num1+$num2")
num=$(perl -e "print $num1+$num2")
num=$(echo $num1 + $num2 | bc)   # Whitespace for echo is important

Puoi anche usare la notazione scientifica (ad esempio 2.5e+2).


Insidie ​​comuni :

  • Quando si imposta una variabile, non è possibile avere spazi bianchi su entrambi i lati =, altrimenti forzerà la shell a interpretare la prima parola come il nome dell'applicazione da eseguire (ad esempio, num=o num)

    num= 1 num =2

  • bce expraspettiamo che ogni numero e operatore sia un argomento separato, quindi gli spazi bianchi sono importanti. Non possono elaborare argomenti come 3+ +4.

    num=`expr $num1+ $num2`


1
Nella prima risposta mi stampa expr: argomento non numerico La seconda non funziona ..
— Nick

1
@Nick: ci hai provato mentre le variabili avevano un nome num1ed num2esistevano e avevano valori interi?
— sorpigal

1
Yew sono esistiti ma l'unica variabile è doppia .. è un problema ??
— Nick,

2
Sì, questo è un problema :) Nota: la $((..))valutazione aritmetica viene eseguita in bash. exprviene eseguito come un processo separato, quindi sarà molto più lento. usa quest'ultimo su sistemi in cui la valutazione aritmica non è supportata (sh! = bash)
— Karoly Horvath,

4
Poiché $((…))è standardizzato da POSIX, dovrebbe diventare sempre più raro ciò che exprè necessario.
— chepner,


30

Ci sono mille modi per farlo. Eccone uno che utilizza dc(una calcolatrice da scrivania con inversione di lucidatura che supporta l'aritmetica di precisione illimitata):

dc <<<"$num1 $num2 + p"

Ma se questo è troppo difficile per te (o la portabilità è importante) potresti dire

echo $num1 $num2 + p | dc

Ma forse sei una di quelle persone che pensano che RPN sia strano e strano; non ti preoccupare! bcè qui per te:

bc <<< "$num1 + $num2"
echo $num1 + $num2 | bc

Detto questo, ci sono alcuni miglioramenti non correlati che potresti apportare alla tua sceneggiatura:

#!/bin/bash

num=0
metab=0

for ((i=1; i<=2; i++)); do
    for j in output-$i-* ; do # 'for' can glob directly, no need to ls
            echo "$j"

             # 'grep' can read files, no need to use 'cat'
            metab=$(grep EndBuffer "$j" | awk '{sum+=$2} END { print sum/120}')
            num=$(( $num + $metab ))
    done
    echo "$num"
done

Come descritto nella FAQ 022 di Bash, Bash non supporta nativamente i numeri in virgola mobile. Se è necessario sommare i numeri in virgola mobile, è necessario l'uso di uno strumento esterno (come bco dc).

In questo caso la soluzione sarebbe

num=$(dc <<<"$num $metab + p")

Per aggiungere accumulare numeri eventualmente in virgola mobile in num.


1
@Sorpigal Thankssss molto .. Posso chiederti qualcos'altro..come posso stampare alla fine non il num ma il num / 10 ??
— Nick,

23

In bash,

 num=5
 x=6
 (( num += x ))
 echo $num   # ==> 11

Nota che bash può gestire solo l'aritmetica dei numeri interi, quindi se il tuo comando awk restituisce una frazione, ti consigliamo di ridisegnare: ecco il tuo codice riscritto un po 'per fare tutta la matematica in awk.

num=0
for ((i=1; i<=2; i++)); do      
    for j in output-$i-*; do
        echo "$j"
        num=$(
           awk -v n="$num" '
               /EndBuffer/ {sum += $2}
               END {print n + (sum/120)}
           ' "$j"
        )
    done
    echo "$num"
done

20

Dimentico sempre la sintassi, quindi vengo su Google, ma poi non trovo mai quella con cui ho familiarità: P. Questo è il più pulito per me e più fedele a quello che mi aspetterei in altre lingue.

i=0
((i++))

echo $i;


15

Mi piace molto anche questo metodo, meno disordine:

count=$[count+1]

1
Perché funziona, comunque? Come lo chiamiamo? Non riesco a trovare documenti su questi.
— skyline75489

4
Meno disordine, ma deprecato.
— Camille Goudeseune,


7

Un altro modo portatile per POSIXconformarsi bash, che può essere definito come una funzione .bashrcper tutti gli operatori aritmetici di convenienza.

addNumbers () {
    local IFS='+'
    printf "%s\n" "$(( $* ))"
}

e chiamalo semplicemente nella riga di comando come,

addNumbers 1 2 3 4 5 100
115

L'idea è di utilizzare Input-Field-Separator (IFS) , una variabile speciale bashutilizzata per la divisione delle parole dopo l'espansione e per dividere le linee in parole. La funzione modifica il valore localmente per utilizzare il carattere di divisione delle parole come operatore di somma+ .

Ricordare che IFSè stato modificato localmente e NON ha effetto sul IFScomportamento predefinito al di fuori dell'ambito della funzione. Un estratto dalla man bashpagina,

La shell tratta ogni carattere di IFS come un delimitatore e divide i risultati delle altre espansioni in parole su questi caratteri. Se IFS non è impostato o il suo valore è esattamente, l'impostazione predefinita, quindi le sequenze di, e all'inizio e alla fine dei risultati delle espansioni precedenti vengono ignorate e qualsiasi sequenza di caratteri IFS non all'inizio o alla fine serve a delimitare parole.

La "$(( $* ))"rappresenta la lista degli argomenti passati da dividere tra +e successivamente il valore della somma è uscita utilizzando la printffunzione. La funzione può essere estesa per aggiungere ambito anche ad altre operazioni aritmetiche.


2
#!/bin/bash

num=0
metab=0

for ((i=1; i<=2; i++)); do      
    for j in `ls output-$i-*`; do
        echo "$j"

        metab=$(cat $j|grep EndBuffer|awk '{sum+=$2} END { print sum/120}') (line15)
        let num=num+metab (line 16)
    done
    echo "$num"
done

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.