Como posso adicionar números em um script Bash?

566

Eu tenho esse script Bash e tive um problema na linha 16. Como posso pegar o resultado anterior da linha 15 e adicioná-lo à variável na linha 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
usuario
fonte
2
Eu acho que você imprimirá um valor zero, mesmo usando a resposta a seguir. Há um truque, por exemplo, subestação de processo, para trazer o valor final de "dentro de um" para "fora de um".
Scott Chu

Respostas:

976

Para números inteiros :

  • Use expansão aritmética :$((EXPR))

    num=$((num1 + num2))
    num=$(($num1 + $num2))       # Also works
    num=$((num1 + 2 + 3))        # ...
    num=$[num1+num2]             # Old, deprecated arithmetic expression syntax
  • Usando o exprutilitário externo . Observe que isso é necessário apenas para sistemas realmente antigos.

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

Para ponto flutuante :

O Bash não suporta isso diretamente, mas existem algumas ferramentas externas que você pode 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

Você também pode usar notação científica (por exemplo 2.5e+2).


Armadilhas comuns :

  • Ao definir uma variável, você não pode ter espaços em branco em nenhum dos lados =, caso contrário, forçará o shell a interpretar a primeira palavra como o nome do aplicativo a ser executado (por exemplo, num=ou num)

    num= 1 num =2

  • bce exprespere cada número e operador como um argumento separado, para que os espaços em branco sejam importantes. Eles não podem processar argumentos como 3+ +4.

    num=`expr $num1+ $num2`

Karoly Horvath
fonte
1
Na primeira resposta que me imprime expr: O argumento não numérico O segundo não trabalho ..
Nick
1
@ Nick: Você tentou isso enquanto as variáveis ​​nomeavam num1e num2existiam e tinham valores inteiros?
sorpigal
1
Teixo eles existem, mas a única variável é dupla .. isso é um problema?
Nick
2
Sim, isso é um problema :) Nota: a $((..))avaliação aritmética é executada no bash. expré executado como um processo separado, então será muito mais lento. usar este último em sistemas onde a avaliação arithemtic não é suportado (! sh = bash)
Karoly Horvath
4
Como $((…))é padronizado pelo POSIX, deve se tornar cada vez mais raro o exprnecessário.
chepner
115

Use a $(( ))expansão aritmética.

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

Consulte o Capítulo 13. Expansão aritmética para obter mais informações.

Steve Prentice
fonte
2
Estranho. Não funciona aqui quando tento calcular 191.003 + 190.
Sigur
2
Não funciona para números com um ponto decimal.
fivedogit 5/04
30

Existem mil e uma maneiras de fazer isso. Aqui está um uso dc(uma calculadora de mesa com polimento reverso que suporta aritmética de precisão ilimitada):

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

Mas se isso é muito tímido para você (ou a portabilidade é importante), você poderia dizer

echo $num1 $num2 + p | dc

Mas talvez você seja uma daquelas pessoas que acha que a RPN é nojenta e estranha; Não se preocupe! bcestá aqui para você:

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

Dito isto, existem algumas melhorias não relacionadas que você pode estar fazendo no seu 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

Conforme descrito na Bash FAQ 022 , o Bash não suporta nativamente números de ponto flutuante. Se você precisar somar números de ponto flutuante, será necessário o uso de uma ferramenta externa (como bcou dc).

Nesse caso, a solução seria

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

Para adicionar números acumulados de ponto flutuante em num.

sorpigal
fonte
1
@ Sorpigal Thankssss muito .. Posso perguntar uma coisa mais .. como posso imprimir no final, não o num, mas o num / 10?
Nick
23

Na festança,

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

Observe que o bash pode lidar apenas com aritmética inteira, portanto, se o seu comando awk retornar uma fração, você precisará redesenhar: aqui está o código reescrito um pouco para fazer todas as contas no 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
Glenn Jackman
fonte
20

Eu sempre esqueço a sintaxe, então venho ao google, mas nunca encontro a que estou familiarizada: P. Isso é o mais limpo para mim e mais fiel ao que eu esperaria em outros idiomas.

i=0
((i++))

echo $i;
ssshake
fonte
18
 #!/bin/bash
read X
read Y
echo "$(($X+$Y))"
Amarjeet Singh
fonte
1
Funciona bem no macOS.
Tmx 17/01/19
15

Eu realmente gosto desse método também, menos confusão:

count=$[count+1]
unixball
fonte
1
Por que isso está funcionando, a propósito? Como chamamos isso? Não consigo encontrar documentos sobre isso.
skyline75489
4
Menos confusão, mas obsoleto.
Camille Goudeseune 27/10/16
7

Outra POSIXmaneira portátil de cumprir bash, que pode ser definida como uma função .bashrcpara todos os operadores aritméticos de conveniência.

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

e chame-o na linha de comando como,

addNumbers 1 2 3 4 5 100
115

A idéia é usar o IFS (Input-Field-Separator) , uma variável especial bashusada para dividir palavras após a expansão e dividir linhas em palavras. A função altera o valor localmente para usar o caractere de divisão de palavras como o operador de soma+ .

Lembre-se de que IFSé alterado localmente e NÃO entra em vigor no IFScomportamento padrão fora do escopo da função. Um trecho da man bashpágina,

O shell trata cada caractere do IFS como um delimitador e divide os resultados das outras expansões em palavras nesses caracteres. Se o IFS estiver desativado ou seu valor for exatamente, o padrão, as seqüências de, e no início e no final dos resultados das expansões anteriores serão ignoradas e qualquer sequência de caracteres do IFS que não esteja no início ou no final serve para delimitar palavras.

O "$(( $* ))"representa a lista de argumentos passados ​​para serem divididos +e, posteriormente, o valor da soma é gerado usando a printffunção A função pode ser estendida para adicionar escopo para outras operações aritméticas também.

Inian
fonte
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
fonte