Parenthesis in expr arithmetic: 3 * (2 + 1)

142,207

Solution 1

Another way to use let bash builtin:

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

Note

As @Stéphane Chazelas pointed out, in bash you should use ((...)) to do arithmetic over expr or let for legibility.

For portability, use $((...)) like @Bernhard answer.

Solution 2

You can use the arithmetic expansion instead.

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

In my personal opinion, this looks a bit nicer than using expr.

From man bash

Arithmetic Expansion Arithmetic expansion allows the evaluation of an arithmetic expression and the substitution of the result. The format for arithmetic expansion is:

         $((expression))

The expression is treated as if it were within double quotes, but a double quote inside the parentheses is not treated specially. All tokens in the expression undergo parameter expansion, string expansion, command substitution, and quote removal. Arithmetic expansions may be nested.

The evaluation is performed according to the rules listed below under ARITHMETIC EVALUATION. If expression is invalid, bash prints a message indicating failure and no substitution occurs.

Solution 3

There's no reason to be using expr for arithmetic in modern shells.

POSIX defines the $((...)) expansion operator. So you can use that in all POSIX compliant shells (the sh of all modern Unix-likes, dash, bash, yash, mksh, zsh, posh, ksh...).

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

ksh also introduced a let builtin which is passed the same kind of arithmetic expression, doesn't expand into something but returns an exit status based on whether the expression resolves to 0 or not, like in expr:

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

However, as the quoting makes it awkward and not very legible (not to the same extent as expr of course), ksh also introduced a ((...)) alternative form:

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

which is a lot more legible and should be used instead.

let and ((...)) are only available in ksh, zsh and bash. The $((...)) syntax should be preferred if portability to other shells is needed, expr is only needed for pre-POSIX Bourne-like shells (typically the Bourne shell or early versions of the Almquist shell).

On the non-Bourne front, there are a few shells with built-in arithmetic operator:

  • csh/tcsh (actually the first Unix shell with arithmetic evaluation built-in):

    @ a = 3 * (2 + 1)
    
  • akanga (based on rc)

    a = $:'3 * (2 + 1)'
    
  • as a history note, the original version of the Almquist shell, as posted on usenet in 1989 had an expr builtin (actually merged with test), but it was removed later.

Solution 4

expr is an external command, it is not special shell syntax. Therefore, if you want expr to see shell special characters, you need to protect them from shell parsing by quoting them. Furthermore, expr needs each number and operator to be passed as a separate parameter. Thus:

expr 3 \* \( 2 + 1 \)

Unless you're working on an antique unix system from the 1970s or 1980s, there is very little reason to use expr. In the old days, shells didn't have a built-in way to perform arithmetic, and you had to call the expr utility instead. All POSIX shells have built-in arithmetic via the arithmetic expansion syntax.

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

The construct $((…)) expands to the result of the arithmetic expression (written in decimal). Bash, like most shells, supports only integer arithmetic modulo 264 (or modulo 232 for older versions of bash and some other shells on 32-bit machines).

Bash offers an additional convenience syntax when you want to perform assignments or to test whether an expression is 0 but don't care about the result. This construct also exists in ksh and zsh but not in plain sh.

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

In addition to integer arithmetic, expr offers a few string manipulation functions. These too are subsumed by features of POSIX shells, except for one: expr STRING : REGEXP tests whether the string matches the specified regexp. A POSIX shell cannot do this without external tools, but bash can with [[ STRING =~ REGEXP ]] (with a different regexp syntax — expr is a classic tool and uses BRE, bash uses ERE).

Unless you're maintaing scripts that run on 20-year-old systems, you don't need to know that expr ever existed. Use shell arithmetic.

Solution 5

Use parenthesis with quotes:

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

The quotes prevent bash from interpreting the parenthesis as bash syntax.

Share:
142,207

Related videos on Youtube

Vitalij
Author by

Vitalij

I am Nicolas Raoul, IT consultant in Tokyo. Feel free to copy/paste the source code from my StackExchange answers, I release it to the public domain.

Updated on September 18, 2022

Comments

  • Vitalij
    Vitalij almost 2 years

    expr does not seem to like parenthesis (used in mathematics to explicit operator priority):

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

    How to express operator priority in bash?

  • Vitalij
    Vitalij almost 10 years
    +1 Even more readable! I posted my question+answer just thinking it would be helpful for my fellow Linux users, but now I am getting a lot of benefit from the other answers :-)
  • chepner
    chepner almost 10 years
    Aside from readability, it also doesn't require forking an extra process to do the arithmetic; it's handled by the shell itself.
  • Stéphane Chazelas
    Stéphane Chazelas almost 10 years
    There's no reason to be using let. It's not any more standard or portable than (( a = 3 * (2 + 1) )) (both come from ksh and are only available in ksh, bash and zsh) and it's less legible or easy to quote. Use a=$((3 * (2 + 1))) to be portable.
  • Stéphane Chazelas
    Stéphane Chazelas almost 10 years
    I'm not saying it's wrong, I'm just saying it should not be used as there are better alternatives (one for legibility ((a = 3 * (2 + 1) )), one for portability a=$((3 * (2 + 1)))), so it's not a note against you or your answer but against it being the selected answer and top-scorer.
  • Stéphane Chazelas
    Stéphane Chazelas almost 10 years
    Note that in POSIX shells, it's subject to word splitting, so it's a good habit to quote it in list contexts.
  • cuonglm
    cuonglm almost 10 years
    @StéphaneChazelas: Updated my answer!
  • G-Man Says 'Reinstate Monica'
    G-Man Says 'Reinstate Monica' almost 10 years
    What Nicolas illustrates but doesn’t explain is that the tokens on the expr command line must be separated by spaces; so; for example, expr 3 "*" "(2" "+" "1)" will not work. (Also, BTW, you probably don’t need to quote the +.)
  • Stéphane Chazelas
    Stéphane Chazelas almost 10 years
    expr foo : '\(.\)' also does text extraction. bash's BASH_REMATCH achieves something similar. It also does string comparison, which POSIX [ does not do (though one could imagine ways to use sort for that).
  • MattBianco
    MattBianco almost 10 years
    I learn something new every day from you, Stéphane. I very much appreciate your POSIX shell knowledge!
  • Gordon
    Gordon about 9 years
    I have always used a=1 $[a+2] or a=1 b=2 $[a+b]. Is their reason to avoid that syntax?
  • Mingye Wang
    Mingye Wang over 8 years
    How about : $((a = a*2))?
  • Sushant
    Sushant over 8 years
    underscore as syntax placeholder --- you a Schemer, @Giles ? :]
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' over 8 years
    @RubyTuesdayDONO I didn't use an underscore here. Are you misreading U+2026 HORIZONTAL ELLIPSIS? If so, try using a bigger font.
  • Sushant
    Sushant over 8 years
    @Giles — okay, yes, it only looks like an underscore because of my font size. for me, "Schemer" is a complement, and it's not like ellipsis versus underscore changes the meaning anyway … no need to get snarky over it :/
  • lordhog
    lordhog almost 8 years
    When I try this at the bash shell I get 'Illegal variable name."
  • Simon Lindholm
    Simon Lindholm over 5 years
    @Gordon: The bash man page says "The old format $[expression] is deprecated and will be removed in upcoming versions of bash."
  • Blaise
    Blaise about 5 years
    What if I have a floating point? My expression is a=$(( -14 + 0.2 * (1+2+3) )). The error token is ".2*(1+2+3)"
  • Stéphane Chazelas
    Stéphane Chazelas about 5 years
    @Blaise, then you'd need a shell that supports floating points in $((...)) like zsh, ksh93 or yash.
  • hackerb9
    hackerb9 almost 3 years
    @Blaise If you need floating point, you can just use an external program, like so a=$(bc <<<"-14 + 0.2 * (1+2+3)")