Posso usar frações / decimais em um script bash?

3

Existe uma certa sintaxe para usar ao usar frações ou decimais em um script bash?

Eu tentei usar o seguinte script (no Ubuntu 12.04):

#!/bin/bash
{
n=9
echo "$n"
for (( i=.5; $i <10; i++ ));
      do
      let "c=$i+1"
      echo $i "+" $c
done
}

Isso funciona com i=1 , mas gera um erro de sintaxe quando eu coloco .5 .

    
por user207701 25.10.2013 / 23:29

6 respostas

5

Sim, o bash só lida com números inteiros, mas você pode rotear o problema, seja por não usar bash (o Python é muito simples de pegar) ou usando bc para manipular os cálculos.

Lembre-se de que sua condição em seu loop não vai funcionar em frações, então você precisará expandir isso também.

step="0.5"

for (( i=0; $i<$(bc<<<"10/$step"); i++ )); do
      echo $(bc<<<"$step * $i")
done

Isso ecoa 0 a 9.5 em 0.5 em incrementos.

    
por Oli 26.10.2013 / 18:03
2

Você pode usar o Zsh .

A maioria dos shells, incluindo Bash, não suportam diretamente cálculos de ponto flutuante e como Oli explicou , a solução típica é chamar bc . No entanto, você tem um número de outras opções .

Um deles é usar o Z Shell . zsh é um Shell estilo shell ( mesma" família " como Bash) que suporta diretamente computações de ponto flutuante. Então, se você estiver disposto a criar um script Zsh em vez de um script Bash, poderá fazer isso alterando a primeira linha do script de #!/bin/bash para #!/bin/zsh .

Assim que fizer isso, funcionará automaticamente! Neste caso , nenhuma modificação adicional no script é necessária. (Às vezes, um recurso Bash e o recurso Zsh correspondente funcionam de modo diferente, portanto, traduzir um script de Bash para Zsh exige que você faça alterações. Embora às vezes um script Bash com erros possa ser correto quando tratado como um script Zsh, devido a diferentes decisões de design no Zsh sobre o que expansions deve ser executado automaticamente.

Esta é a saída:

9
0.5000000000 + 1.5000000000
1.5000000000 + 2.5000000000
2.5000000000 + 3.5000000000
3.5000000000 + 4.5000000000
4.5000000000 + 5.5000000000
5.5000000000 + 6.5000000000
6.5000000000 + 7.5000000000
7.5000000000 + 8.5000000000
8.5000000000 + 9.5000000000
9.5000000000 + 10.5000000000

Você precisará do Zsh instalado para ser executado. O Zsh geralmente não vem pré-instalado no Ubuntu, mas é fornecido pelo pacote zsh . Se você não tiver zsh , verá um erro como:

-bash: ./your-script: /bin/zsh: bad interpreter: No such file or directory

O pacote zsh do Ubuntu oferece /bin/zsh e /usr/bin/zsh . Se você quer que seu script seja portável para outros sistemas que tenham Zsh mas em lugares diferentes, então você pode querer usar este hashbang line no topo do seu script:

#!/usr/bin/env zsh

Se o usuário puder executar o Zsh digitando zsh e pressionando Enter , então colocar isso no início de um script normalmente funcionará (e executará o mesmo executável zsh que o usuário executaria ). Isso não funciona em 100% dos sistemas - tecnicamente, env em si não tem que ser fornecido em /usr/bin - mas na prática é bastante raro para ele falhar, desde que zsh esteja no usuário $PATH .

Para mais informações sobre avaliação aritmética em Zsh, incluindo aritmética de ponto flutuante, veja 11 Avaliação aritmética no Manual do Z Shell .

Diminuindo a precisão da saída

Uma variável de ponto flutuante x em Zsh pode mostrar mais precisão do que você deseja quando você a expande como $x :

% ((x=.5))
% echo "$x"
0.5000000000

Uma maneira de expandi-lo sem esses zeros extras é usar a expansão aritmética:

% echo "$((x))"
0.5

Você também pode usar o comando printf para exibir um valor com precisão limitada:

% printf '%.2f\n' "$x"
0.50

Isso funciona em outros shells também, mesmo quando o shell não suporta aritmética de ponto flutuante. (Algumas shells não têm um printf embutido, mas você ainda pode usar esse comando porque uma versão executável independente está instalada em /usr/bin/printf .) O comando printf não irá executar outra aritmética, além do arredondamento.

Mesmo em situações em que você não precisa de formatação numérica, printf é geralmente uma escolha melhor que echo em scripts de shell .

Caso você esteja interessado, o manual do Zsh (como mencionado acima) explica como precisamente os valores de ponto flutuantes são realmente armazenados. (Geralmente é IEEE 754 binary64 .)

Você pode usar uma sintaxe mais simples (no Bash também).

Seu script está bem, mas você pode simplificar sua sintaxe para melhorar a legibilidade:

  • Seu script não precisa de o agrupamento fornecido por { e } .
  • Um ; é redundante no final de uma linha.
  • Por dentro de (( )) , $i pode ser apenas escrito i .
  • Você pode usar (( )) em vez de let . Sem um $ inicial, ele avalia sem se expandir para o resultado. Nem todos os shells suportam este formulário de não expansão, mas Bash e Zsh (e alguns outros) fazem isso.
  • É bom citar quando você não quer que a divisão de palavras ocorra mesmo quando isso é opcional (como neste caso). Mas você pode incluir mais de uma expansão no mesmo intervalo de texto com aspas duplas.

Deixando de fora o pouco sobre n no começo (você pode muito bem precisar disso para o seu propósito, mas não sei dizer para que serve), eu sugiro:

#!/bin/zsh

for ((i = .5; i < 10; i++))
do
    ((c = i + 1))
    echo "$i + $c"
done

Ou, se você não quiser que esses zeros extras sejam impressos:

#!/bin/zsh

for ((i = .5; i < 10; i++))
do
    echo "$((i)) + $((i + 1))"
done

Isto imprime:

0.5 + 1.5
1.5 + 2.5
2.5 + 3.5
3.5 + 4.5
4.5 + 5.5
5.5 + 6.5
6.5 + 7.5
7.5 + 8.5
8.5 + 9.5
9.5 + 10.5

Finalmente, para abordar um ponto de possível confusão:

  • Não há problema em atribuir i com i = .5 , porque isso foi avaliado como uma expressão aritmética, devido a aparecer dentro de (( )) e, portanto, espaços em torno de = foram permitidos. Os espaços não são necessários e você não precisa usá-los, mas pode fazê-lo. (Eu fiz isso.)
  • Fora de uma expressão aritmética, atribuir uma variável shell requer que não haja espaços em ambos os lados de = . Ou seja, normalmente você deve escrever var=val , não var = val . Da mesma forma, fora de uma expressão aritmética, acessando o valor de uma variável de shell requer $ (por exemplo, $i , ${i} ).
por Eliah Kagan 02.07.2017 / 11:51
1

Vou apontar que, embora bash esteja limitado a matemática inteira, ksh93 não é. Então, este (exemplo mais simples) funciona bem:

$ for ((i=0.5; i<3; i=$((i+1)))); do print $i ; done
0.5
1.5
2.5

Usamos i=$((i+1)) em vez de i++ porque ++ só funciona com números inteiros.

Então, acho que é isso que o questionador original quer (exceto, é claro, que isso não está sendo feito com bash ):

$ for ((i=0.5; i<10; i=$((i+1)))); do c=$((i+1)) ; print $i + $c ; done
0.5 + 1.5
1.5 + 2.5
2.5 + 3.5
3.5 + 4.5
4.5 + 5.5
5.5 + 6.5
6.5 + 7.5
7.5 + 8.5
8.5 + 9.5
9.5 + 10.5

Isso tudo é feito dentro do próprio shell, sem necessidade de comandos externos. Se houver um requisito para usar bash , as sugestões que usam comandos externos são o caminho a seguir.

    
por sneezy 29.09.2017 / 17:41
0
  

O próprio Bash não suporta números de ponto flutuante, mas existe um programa chamado bc que pode fazer aritmética decimal.

Seu script deve ser reescrito para usar o BC (também conhecido como Melhor Calculadora) ou outro outro utilitário . Então, como você pode fazer isso ? Não há como usar o for loop, já que o próprio bash embutido não suporta pontos flutuantes. Você usa outra variável para controlar dentro de seu loop e usa 1 (ou 0 ) como ponto de partida.

    
por Braiam 26.10.2013 / 01:54
0

Existe uma maneira de falsificar frações, um pouco desajeitado, mas é útil em alguns casos:

#!/bin/bash
{
n=9
echo "$n"

for (( i=5; $i <100; i+=10 ))
do
    let "c=$i+10"
    echo "$(( $i / 10 )).$(( $i % 10 )) + $(( $c / 10 )).$(( $c % 10 ))"
done
}

Com div / módulo você também pode fazer alguns cálculos interessantes (leia-se: desajeitados).

    
por thom 29.10.2013 / 00:50
0
#!/bin/bash
{ 
n=9; 
echo "$n"; 
for i in 'seq .5 10';       
do       
c='bc <<< $i+1';       
echo $i "+" $c; done; 
}
    
por mahendra 02.07.2017 / 08:04