Wie kann ich einem Bash-Skript Zahlen hinzufügen?

566

Ich habe dieses Bash-Skript und hatte ein Problem in Zeile 16. Wie kann ich das vorherige Ergebnis von Zeile 15 nehmen und es der Variablen in Zeile 16 hinzufügen?

#!/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
Nick
quelle
2
Ich denke, Sie werden einen Nullwert drucken, selbst wenn Sie die folgende Antwort verwenden. Es gibt einen Trick, z. B. eine Prozesssubstitution, um den Endwert von "inside num" auf "external num" zu bringen.
Scott Chu

Antworten:

975

Für ganze Zahlen :

  • Verwenden Sie die arithmetische Erweiterung :$((EXPR))

    num=$((num1 + num2))
    num=$(($num1 + $num2))       # Also works
    num=$((num1 + 2 + 3))        # ...
    num=$[num1+num2]             # Old, deprecated arithmetic expression syntax
  • Verwenden des externen exprDienstprogramms. Beachten Sie, dass dies nur für wirklich alte Systeme benötigt wird.

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

Für Gleitkomma :

Bash unterstützt dies nicht direkt, aber es gibt einige externe Tools, die Sie verwenden können:

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

Sie können auch die wissenschaftliche Notation verwenden (z. B. 2.5e+2).


Häufige Fallstricke :

  • Wenn Sie eine Variable festlegen, darf auf keiner Seite ein Leerzeichen =stehen. Andernfalls wird die Shell gezwungen, das erste Wort als Namen der auszuführenden Anwendung zu interpretieren (z. B. num=oder num).

    num= 1 num =2

  • bcund exprerwarten Sie jede Zahl und jeden Operator als separates Argument, daher ist Leerzeichen wichtig. Sie können Argumente wie nicht verarbeiten 3+ +4.

    num=`expr $num1+ $num2`

Karoly Horvath
quelle
1
In der ersten Antwort druckt es mich Ausdruck: nicht numerisches Argument Das zweite funktioniert nicht ..
Nick
1
@ Nick: Haben Sie dies versucht, während Variablen benannt num1und num2existierten und ganzzahlige Werte hatten?
Sorpigal
1
Eibe, sie existieren, aber die eine Variable ist doppelt. Ist das ein Problem?
Nick
2
Ja, das ist ein Problem :) Hinweis: Die $((..))arithmetische Auswertung wird in Bash ausgeführt. exprwird als separater Prozess ausgeführt, daher wird es viel langsamer. Verwenden Sie letzteres auf Systemen, auf denen die arithemtische Auswertung nicht unterstützt wird (sh! = bash)
Karoly Horvath
4
Da $((…))es von POSIX standardisiert wird, sollte es immer seltener werden, exprwas notwendig ist.
Chepner
115

Verwenden Sie die $(( ))arithmetische Erweiterung.

num=$(( $num + $metab ))

Weitere Informationen finden Sie in Kapitel 13. Arithmetische Erweiterung .

Steve Prentice
quelle
2
Seltsam. Es funktioniert hier nicht, wenn ich versuche zu berechnen 191.003 + 190.
Sigur
2
Funktioniert nicht für Zahlen mit einem Dezimalpunkt.
Fivedogit
30

Es gibt tausend und eine Möglichkeit, dies zu tun. Hier ist eine Verwendung dc(ein Reverse-Polish-Tischrechner, der Arithmetik mit unbegrenzter Genauigkeit unterstützt):

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

Aber wenn Ihnen das zu schüchtern ist (oder wenn es um Portabilität geht), können Sie sagen

echo $num1 $num2 + p | dc

Aber vielleicht gehören Sie zu den Leuten, die RPN für eklig und komisch halten. Mach dir keine Sorgen! bcist für dich da:

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

Es gibt jedoch einige unabhängige Verbesserungen, die Sie an Ihrem Skript vornehmen könnten:

#!/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

Wie in Bash FAQ 022 beschrieben , unterstützt Bash Gleitkommazahlen nicht nativ. Wenn Sie Gleitkommazahlen summieren müssen, verwenden Sie ein externes Werkzeug (wie bcoderdc ) erforderlich.

In diesem Fall wäre die Lösung

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

Zum Hinzufügen von akkumulierten Gleitkommazahlen in num.

sorpigal
quelle
1
@Sorpigal Vielen Dank. Kann ich dich noch etwas fragen. Wie kann ich am Ende nicht die Nummer, sondern die Nummer / 10 drucken?
Nick
23

In Bash,

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

Beachten Sie, dass bash nur Ganzzahlarithmetik verarbeiten kann. Wenn Ihr Befehl awk also einen Bruch zurückgibt, sollten Sie ihn neu gestalten: Hier ist Ihr Code, der ein wenig umgeschrieben wurde, um alle Berechnungen in awk durchzuführen.

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
Glenn Jackman
quelle
20

Ich vergesse immer die Syntax, also komme ich zu Google, aber dann finde ich nie die, mit der ich vertraut bin: P. Dies ist das sauberste für mich und entspricht mehr dem, was ich in anderen Sprachen erwarten würde.

i=0
((i++))

echo $i;
ssshake
quelle
18
 #!/bin/bash
read X
read Y
echo "$(($X+$Y))"
Amarjeet Singh
quelle
1
Funktioniert gut in macOS.
Tmx
15

Ich mag diese Methode auch sehr, weniger Unordnung:

count=$[count+1]
Unixball
quelle
1
Warum funktioniert das übrigens? Wie nennen wir das? Ich kann keine Dokumente darüber finden.
skyline75489
4
Weniger Unordnung, aber veraltet.
Camille Goudeseune
7

Eine weitere tragbare POSIXkompatible Methode bash, die als Funktion .bashrcfür alle arithmetischen Operatoren der Bequemlichkeit definiert werden kann.

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

und nenne es einfach in der Kommandozeile als,

addNumbers 1 2 3 4 5 100
115

Die Idee ist, den Eingabefeld-Separator (IFS) zu verwenden , eine spezielle Variable, bashdie zum Teilen von Wörtern nach der Erweiterung und zum Teilen von Zeilen in Wörter verwendet wird. Die Funktion ändert den Wert lokal, um das Wortteilungszeichen als Summenoperator zu verwenden+ .

Denken Sie daran, dass das IFSlokal geändert wird und sich NICHT auf das Standardverhalten IFSaußerhalb des Funktionsumfangs auswirkt . Ein Auszug aus der man bashSeite,

Die Shell behandelt jedes Zeichen von IFS als Trennzeichen und teilt die Ergebnisse der anderen Erweiterungen in Wörter auf diesen Zeichen auf. Wenn IFS nicht gesetzt ist oder sein Wert genau der Standardwert ist, werden Sequenzen von, und am Anfang und Ende der Ergebnisse der vorherigen Erweiterungen ignoriert, und jede Sequenz von IFS-Zeichen, die nicht am Anfang oder Ende steht, dient zur Abgrenzung Wörter.

Das "$(( $* ))"stellt die Liste der Argumente dar, die übergeben werden sollen, um geteilt zu werden, +und später wird der Summenwert unter Verwendung der printfFunktion ausgegeben . Die Funktion kann erweitert werden, um auch anderen arithmetischen Operationen Spielraum zu geben.

Inian
quelle
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
Milen John Thomas
quelle