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.