Parênteses na aritmética expr: 3 * (2 + 1)

60

expr parece não gostar de parênteses (usado em matemática para prioridade explícita do operador):

expr 3 * (2 + 1)
bash: syntax error near unexpected token `('

Como expressar a prioridade do operador no bash?

Nicolas Raoul
fonte

Respostas:

40

Outra maneira de usar o letbash builtin:

$ let a="3 * (2 + 1)"
$ printf '%s\n' "$a"
9

Nota

Como @ Stéphane Chazelas apontou , bashvocê deve ((...))fazer aritmética acima exprou letpor legibilidade.

Para portabilidade, use $((...))como a resposta @ Bernhard .

cuonglm
fonte
11
+1 Ainda mais legível! Eu postei minha pergunta + responder só de pensar que seria útil para os meus colegas usuários de Linux, mas agora eu estou recebendo um monte de beneficiar das outras respostas :-)
Nicolas Raoul
3
Não há razão para estar usando let. Não é mais padrão ou portátil do que (( a = 3 * (2 + 1) ))(ambos vêm kshe estão disponíveis apenas em ksh, bash e zsh) e é menos legível ou fácil de citar. Use a=$((3 * (2 + 1)))para ser portátil.
Stéphane Chazelas
2
Não estou dizendo que está errado, estou apenas dizendo que não deve ser usado, pois existem alternativas melhores (uma para legibilidade ((a = 3 * (2 + 1) ))e outra para portabilidade a=$((3 * (2 + 1)))); portanto, não é uma nota contra você ou sua resposta, mas contra a resposta selecionada. e melhor marcador.
Stéphane Chazelas 12/08
@ StéphaneChazelas: Atualizado minha resposta!
cuonglm
Eu sempre usei a=1 $[a+2]ou a=1 b=2 $[a+b]. É o motivo deles para evitar essa sintaxe?
Gordon
74

Você pode usar a expansão aritmética.

echo "$(( 3 * ( 2 + 1 ) ))"
9

Na minha opinião pessoal, isso parece um pouco melhor do que usar expr.

De man bash

Expansão aritmética A expansão aritmética permite a avaliação de uma expressão aritmética e a substituição do resultado. O formato para expansão aritmética é:

         $((expression))

A expressão é tratada como se estivesse entre aspas duplas, mas uma aspas dupla entre parênteses não é tratada especialmente. Todos os tokens na expressão passam por expansão de parâmetro, expansão de cadeia, substituição de comando e remoção de cotação. Expansões aritméticas podem ser aninhadas.

A avaliação é realizada de acordo com as regras listadas abaixo em AVALIAÇÃO ARITMÉTICA. Se a expressão for inválida, o bash imprime uma mensagem indicando falha e nenhuma substituição ocorre.

Bernhard
fonte
11
Além da legibilidade, ele também não requer um processo extra para fazer a aritmética; é tratado pela própria concha.
chepner
Observe que, nos shells POSIX, ele está sujeito à divisão de palavras; portanto, é um bom hábito citá-lo em contextos de lista.
Stéphane Chazelas
Quando tento isso no shell bash, recebo 'Nome da variável ilegal. "
lordhog 21/09/16
40

Não há razão para usar expraritmética em conchas modernas.

POSIX define o $((...))operador de expansão. Então você pode usá-lo em todos os shells compatíveis com POSIX ( shtodos os gostos modernos do Unix, dash, bash, yash, mksh, zsh, posh, ksh ...).

a=$(( 3 * (2 + 1) ))
a=$((3*(2+1)))

kshtambém introduziu um letbuilt-in que recebe o mesmo tipo de expressão aritmética, não se expande para algo, mas retorna um status de saída com base no fato de a expressão ser resolvida como 0 ou não, como em expr:

if let 'a = 3 * (2 + 1)'; then
  echo "$a is non-zero"
fi

No entanto, como a citação o torna estranho e pouco legível (não na mesma extensão que é exprclaro), kshtambém introduziu uma ((...))forma alternativa:

if (( a = 3 * (2 + 1) )) && (( 3 > 1 )); then
  echo "$a is non-zero and 3 > 1"
fi
((a+=2))

que é muito mais legível e deve ser usado.

lete ((...))estão disponíveis apenas em ksh, zshe bash. A $((...))sintaxe deve ser preferida se a portabilidade para outros shells for necessária, exprapenas necessária para shells pré-POSIX tipo Bourne (normalmente o shell Bourne ou versões anteriores do shell Almquist).

Na frente não Bourne, existem algumas conchas com o operador aritmético embutido:

  • csh/ tcsh(na verdade, o primeiro shell Unix com avaliação aritmética integrada):

    @ a = 3 * (2 + 1)
  • akanga(com base em rc)

    a = $:'3 * (2 + 1)'
  • como uma nota de histórico, a versão original do shell Almquist, publicada na usenet em 1989, tinha um exprbuilt-in (na verdade, foi mesclado com test), mas foi removida mais tarde.

Stéphane Chazelas
fonte
Eu aprendo algo novo todos os dias com você, Stéphane. Aprecio muito seu conhecimento sobre o shell POSIX!
precisa saber é o seguinte
Que tal : $((a = a*2))?
Arthur2e5
E se eu tiver um ponto flutuante? Minha expressão é a = $ ((-14 + 0,2 * (1 + 2 + 3)))). O token de erro é ".2 * (1 + 2 + 3)"
Blaise
@ Blaise, você precisaria de um shell que suporte pontos flutuantes $((...))como zsh, ksh93 ou yash.
Stéphane Chazelas
16

expré um comando externo, não é uma sintaxe especial do shell. Portanto, se você deseja exprver caracteres especiais do shell, é necessário protegê-los da análise do shell, citando-os. Além disso, exprprecisa que cada número e operador sejam passados ​​como um parâmetro separado. Portanto:

expr 3 \* \( 2 + 1 \)

A menos que você esteja trabalhando em um sistema unix antigo das décadas de 1970 ou 1980, há muito pouco motivo para usar expr. Antigamente, os shells não tinham uma maneira integrada de executar aritmética, e você precisava chamar o exprutilitário. Todos os shells POSIX possuem aritmética integrada por meio da sintaxe de expansão aritmética .

echo "$((3 * (2 + 1)))"

A construção se $((…))expande para o resultado da expressão aritmética (escrita em decimal). O Bash, como a maioria dos shells, suporta apenas o módulo aritmético inteiro 2 64 (ou o módulo 2 32 para versões mais antigas do bash e alguns outros shells em máquinas de 32 bits).

O Bash oferece uma sintaxe de conveniência adicional quando você deseja executar atribuições ou testar se uma expressão é 0, mas não se importa com o resultado. Essa construção também existe no ksh e no zsh, mas não no sh simples.

((x = 3 * (2+1)))
echo "$x"
if ((x > 3)); then 

Além da aritmética inteira, exproferece algumas funções de manipulação de string. Eles também são incluídos nos recursos dos shells POSIX, exceto um: expr STRING : REGEXPtesta se a string corresponde ao regexp especificado. Um shell POSIX não pode fazer isso sem ferramentas externas, mas o bash pode com [[ STRING =~ REGEXP ]](com uma sintaxe regexp diferenteexpré uma ferramenta clássica e usa BRE, o bash usa ERE).

A menos que você esteja mantendo scripts executados em sistemas de 20 anos, não é necessário saber que isso exprjá existiu. Use aritmética de casca.

Gilles 'SO- parar de ser mau'
fonte
expr foo : '\(.\)'também faz extração de texto. bash's BASH_REMATCHconsegue algo similar. Também faz comparação de strings, o que o POSIX [não faz (embora se possa imaginar maneiras de usá sort-lo).
Stéphane Chazelas
sublinhado como marcador de posição de sintaxe --- você é um Schemer, @Giles? :]
RubyTuesdayDONO
11
@RubyTuesdayDONO Não usei um sublinhado aqui. Você está interpretando mal ELLIPSIS HORIZONTAL? Nesse caso, tente usar uma fonte maior.
Gilles 'SO- stop be evil'
@ Giles - ok, sim, parece apenas um sublinhado por causa do meu tamanho da fonte. para mim "Schemer" é um complemento, e não é como reticências contra sublinhado muda o significado de qualquer maneira ... há necessidade de obter snarky sobre ele: /
RubyTuesdayDONO
12

Use parênteses com aspas:

expr 3 '*' '(' 2 '+' 1 ')'
9

As aspas impedem que o bash interprete os parênteses como sintaxe do bash.

Nicolas Raoul
fonte
2
O que Nicolas ilustra, mas não explica, é que os tokens na exprlinha de comando devem ser separados por espaços; tão; por exemplo, expr 3 "*" "(2" "+" "1)" não vai funcionar . (Também, BTW, você provavelmente não precisa citar o +.)
G-Man diz 'Reinstate Monica'
Os parênteses não são como palavras-chave whilee [[são sintaxe. Se fossem palavras-chave, não seriam interpretadas como tal nos argumentos de comando. Você precisa de aspas para que o bash não as analise, mas veja uma string literal.
Gilles 'SO- stop be evil'
1

Se você tem bc ..

echo '3 * (2 + 1)'|bc 
9                                                                    
roubar
fonte