Problema com o uso de expr

1

Eu tenho duas seqüências

str1="( 1 + 2 + 3 + 4 + 5 ) / 3 + 5"

e

str2="( 1 + 2 + 3 + 4 + 5 ) / 3 + 5 * 2"

O comando

result=' expr $str1'

retorna um valor correto, enquanto

result=' expr $str2'

retorna expr: syntax error

    
por Mateusz 10.04.2015 / 00:52

2 respostas

2

O seguinte irá produzir resultados errados:

str2="( 1 + 2 + 3 + 4 + 5 ) / 3 + 5 * 2"
expr $str2

O problema é que o shell considera * como sendo um glob de arquivo curinga e o substituirá por uma lista de arquivos no diretório atual. Não é isso que você quer.

expr é arcaico. Uma solução mais moderna usaria a forma $((...)) do shell para aritmética:

$ str2="( 1 + 2 + 3 + 4 + 5 ) / 3 + 5 * 2"
$ echo "$((str2))"
15

O shell só faz aritmética inteira. Se você realmente queria resultados precisos de ponto flutuante (que são os mesmos aqui), use bc :

$ echo "$str2" | bc -l
15.00000000000000000000

Observe que $str2 está entre aspas duplas para evitar danos no shell.

    
por 10.04.2015 / 01:06
1

Quando você expande uma variável fora das aspas duplas, como em expr $str2 , ocorrem as seguintes ações:

  1. Pegue o valor da variável. O resultado é uma string.
  2. Divida o valor em partes delimitadas por espaço em branco.¹ O resultado é uma lista de strings.
  3. Interprete cada elemento da lista como padrão curinga, ou seja, globbing . Se corresponder a arquivos, substitua o elemento pela lista de correspondências. Se não corresponder a nenhum arquivo, deixe o elemento sozinho.

Por exemplo, o valor da sua variável contém a palavra * , que é substituída pela lista de nomes de arquivos no diretório atual.

Em geral, sempre coloque aspas duplas em torno de substituições de variáveis e substituições de comandos: "$stuff" , "'stuff'" . Veja Por que meu script de shell engasgue-se com espaços em branco ou outros caracteres especiais? Apenas deixe de fora as aspas se você souber e entender por que você precisa deixar de fora as aspas.

Nesse caso, é necessário que a etapa 2 (divisão) aconteça, porque expr precisa dos operadores e operandos em argumentos separados. Mas o passo 3 (tratando cada palavra como um padrão curinga de nome de arquivo) não deve acontecer. Você pode fazer isso desligando globbing:

result='set -f; expr $str2'

Em ksh ou bash, use um array ao invés de uma string - mas nesses shells você não teria nenhum uso para expr .

A menos que você precise que seu script seja portável para os antigos shell Bourne, você não precisa usar expr . Expressões aritméticas são um recurso POSIX padrão.

result=$(($str2))

¹ Os caracteres do separador podem ser configurados por meio da variável IFS .

    
por 10.04.2015 / 01:49