¿Cómo puedo agregar números en un script Bash?


567

Tengo este script de Bash y tuve un problema en la línea 16. ¿Cómo puedo tomar el resultado anterior de la línea 15 y agregarlo a la variable en la línea 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
Creo que imprimirá un valor cero incluso utilizando la siguiente respuesta. Hay un truco, p. Ej., La sustitución del proceso, para llevar el valor final de "número interno" a "número externo".
Scott Chu

Respuestas:


977

Para enteros :

  • Usar expansión aritmética :$((EXPR))

    num=$((num1 + num2))
    num=$(($num1 + $num2))       # Also works
    num=$((num1 + 2 + 3))        # ...
    num=$[num1+num2]             # Old, deprecated arithmetic expression syntax
  • Usando la exprutilidad externa . Tenga en cuenta que esto solo es necesario para sistemas realmente antiguos.

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

Para coma flotante :

Bash no lo admite directamente, pero hay un par de herramientas externas que puede usar:

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

También puede usar notación científica (por ejemplo 2.5e+2).


Errores comunes :

  • Al establecer una variable, no puede tener espacios en blanco a ambos lados =, de lo contrario, forzará al shell a interpretar la primera palabra como el nombre de la aplicación que se ejecutará (por ejemplo, num=o num)

    num= 1 num =2

  • bcy exprespere cada número y operador como un argumento separado, por lo que el espacio en blanco es importante. No pueden procesar argumentos como 3+ +4.

    num=`expr $num1+ $num2`


1
En la primera respuesta, me imprime expr: argumento no numérico. La segunda no funciona ..
Nick

1
@Nick: ¿Intentaste esto mientras las variables nombradas num1y num2existían y tenían valores enteros?
sorigal

1
Tejo que existen, pero la única variable es doble ... ¿eso es un problema?
Nick

2
Sí, eso es un problema :) Nota: la $((..))evaluación aritmética se ejecuta en bash. exprse ejecuta como un proceso separado, por lo que será mucho más lento. use el último en sistemas donde no se admite la evaluación aritmética (sh! = bash)
Karoly Horvath

44
Como $((…))está estandarizado por POSIX, debería ser cada vez más raro que exprsea ​​necesario.
chepner


30

Hay mil y una formas de hacerlo. Aquí hay uno que usa dc(una calculadora de escritorio de pulido inverso que admite aritmética de precisión ilimitada):

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

Pero si eso es demasiado tímido para ti (o la portabilidad es importante), podrías decir

echo $num1 $num2 + p | dc

Pero tal vez eres una de esas personas que piensa que RPN es asqueroso y extraño; no te preocupes! bcestá aquí para ti:

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

Dicho esto, hay algunas mejoras no relacionadas que podría estar haciendo en su script:

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

Como se describe en la Pregunta frecuente 022 de Bash, Bash no admite de forma nativa números de coma flotante. Si necesita sumar números de coma flotante, se requiere el uso de una herramienta externa (como bco dc).

En este caso la solución sería

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

Para agregar acumular números de punto flotante posiblemente en num.


1
@Sorpigal Muchas gracias ... ¿Puedo preguntarte algo más ... ¿cómo puedo imprimir al final no el número sino el número / 10?
Nick

23

En bash,

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

Tenga en cuenta que bash solo puede manejar aritmética de enteros, por lo que si su comando awk devuelve una fracción, entonces querrá rediseñar: aquí está su código reescrito un poco para hacer todas las matemáticas en 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

Siempre olvido la sintaxis, así que vengo a google, pero nunca encuentro la que conozco: P. Esto es lo más limpio para mí y más fiel a lo que esperaría en otros idiomas.

i=0
((i++))

echo $i;


15

También me gusta mucho este método, menos desorden:

count=$[count+1]

1
¿Por qué está funcionando, por cierto? ¿Cómo llamamos a esto? No puedo encontrar documentos sobre estos.
skyline75489

44
Menos desorden, pero en desuso.
Camille Goudeseune


7

Otra forma portátil POSIXde hacerlo bash, que se puede definir como una función .bashrcpara todos los operadores aritméticos de conveniencia.

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

y solo llámalo en la línea de comandos como,

addNumbers 1 2 3 4 5 100
115

La idea es usar el Separador de campo de entrada (IFS) , una variable especial que se bashusa para dividir palabras después de la expansión y para dividir líneas en palabras. La función cambia el valor localmente para usar el carácter de división de palabras como operador de suma+ .

Recuerde que IFSse cambia localmente y NO tiene efecto en el IFScomportamiento predeterminado fuera del alcance de la función. Un extracto de la man bashpágina,

El shell trata cada carácter de IFS como un delimitador y divide los resultados de las otras expansiones en palabras sobre estos caracteres. Si IFS no está establecido, o su valor es exactamente, el valor predeterminado, entonces las secuencias de, y al principio y al final de los resultados de las expansiones anteriores se ignoran, y cualquier secuencia de caracteres IFS que no esté al principio o al final sirve para delimitar palabras.

El "$(( $* ))"representa la lista de argumentos que se pasan a ser dividida por +y más tarde el valor de la suma es de salida utilizando la printffunción. La función se puede ampliar para agregar alcance para otras operaciones aritméticas también.


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

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.