$ @ usado em um loop para matemática

1

Estou tentando escrever um script Bash que use uma lista de números como argumentos de linha de comando e, em seguida, mostre a soma desses números.

Assim, o script seria executado como script.sh 1 555 22 122 66 e, em seguida, somaria todos eles. Eu sei que eles precisam ser passados para a variável $@ e, em seguida, provavelmente passar por um loop, mas eu não entendo como converter o conteúdo de $@ para um int para fazer as contas. Eu tentei fazer isso:

#!/bin/bash
for i in $@
do
    $@+$@
    echo "the total is '$@'"
done
    
por DanMan3395 06.07.2018 / 02:06

5 respostas

8

Em geral, um argumento é convertido em um inteiro automaticamente se usado dentro de uma expansão aritmética $((...)) . Este loop irá somar todos os argumentos:

for x; do sum=$((sum+x)); done; echo "$sum"

O shell armazena em cache todos os argumentos em locais de memória separados, como um programa c lida com um array argv []. O usuário do shell não precisa lidar diretamente com esse array, o shell ajuda atribuindo-os a $ 1, $ 2, $ 3, etc. O shell também abstrai essa lista como "$ @". E, finalmente, a sintaxe for x é uma abreviação de for x in "$@" para fazer loop em todos os argumentos.

Isso é assumir que os argumentos são números decimais que não começam com zero, números octais começando com zero ou números hexadecimais que começam com 0x e que a soma total não estourará (2 ^ 63-1 em 64 bits sistemas)

Esta lista:

$ ./script 12 021 0xab

Imprime 200 (o resultado decimal).

    
por 06.07.2018 / 02:44
8

Você pode fazer isso com o seguinte:

tr ' ' '+' <<<"$@" | bc

Leva todos os argumentos passados e substitui o espaço em branco com o sinal + e, em seguida, canaliza isso para bc .

    
por 06.07.2018 / 02:41
3

Analise os argumentos um por um:

total=0
while [ -n "$1" ]; do
  total=$((total + "$1"))
  shift
done

Ou use for loop:

total=0
for argument; do
  total=$((total + "$argument"))
done
    
por 06.07.2018 / 02:12
2

Além de $(()) , como outras respostas já usaram, você também pode usar (()) e usar += assim:

sum=0
for x; do
  (( sum += x ))
done
echo $sum

Também pode ajudar a explicar o que você estava fazendo no código fornecido. Basicamente, você divide cada argumento em espaço em branco (porque não citou duas vezes $@ em for i in $@ ) e, em seguida, passou pelo resultado. Então você ignorou a atribuição que você fez, e novamente dividiu os argumentos no espaço em branco mais duas vezes e uniu o último argumento do primeiro conjunto e o primeiro argumento do segundo conjunto com um + então chamou o comando nomeado pelo primeiro argumento com o resto dos argumentos como argumentos. Por exemplo, se esse script foi nomeado sum e você o chamou assim: sum "1 2" 3 , que $@+$@ teria tentado chamar o comando 1 (mesmo procurando /bin/1 ) assim: 1 "2" "3+1" "2" "3" . Isso provavelmente teria resultado em advertir algo como bash: 1: command not found , 3 vezes (uma vez para cada argumento). Então, porque você escreveu o echo dentro do loop, ele tentaria reportar o total uma vez para cada argumento. Na realidade, ele tentaria executar 1 "2" "3" com 1 sendo interpretado como um comando novamente por causa dos backticks na string. Isso também irá resultar em aviso bash que o comando não foi encontrado. Como não havia comando, não haverá saída e echo apenas ecoará "o total é". E então o resultado completo é:

$ sum "1 2" 3
bash: 1: command not found
bash: 1: command not found
the total is 
bash: 1: command not found
bash: 1: command not found
the total is 
bash: 1: command not found
bash: 1: command not found
the total is 
    
por 06.07.2018 / 03:33
1
#!/bin/sh

IFS='+'
printf '%s\n' "$*" | bc

Teste

$ ./script.sh 1 2 3 -1 30 0.1
35.1

O "$*" expandirá para os parâmetros posicionais (os argumentos da linha de comando) delimitados pelo primeiro caractere da variável IFS shell. Colocamos IFS em um sinal de mais e passamos a string para bc para avaliação.

Se apenas aritmética inteira for necessária:

#!/bin/sh

IFS='+'
printf '%d\n' "$(( $* ))"
    
por 06.07.2018 / 11:34