1

I've got two strings

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

and

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

The command

result=` expr $str1`

returns a correct value, while

result=` expr $str2`

returns expr: syntax error

Rui F Ribeiro
  • 56,709
  • 26
  • 150
  • 232
Mateusz
  • 11
  • yes. quote it. expr "$str2". Or use your shell: str='(1+2+3+4+5)/3+5'; echo "$(($str))" "$(($str*2))" – mikeserv Apr 09 '15 at 22:58
  • Do you have 5 * 2 or 5 \* 2 in str2? This isn't clear from what you posted. Please take a minute to go familiarize yourself with the formatting features on this site; you can see a guide by browsing through the buttons above the edit box and reading the help available by clicking on the ? button. – Gilles 'SO- stop being evil' Apr 09 '15 at 23:13

2 Answers2

2

The following will produce wrong results:

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

The problem is that the shell considers * to be a wildcard file glob and will replace it with a list of files in the current directory. This is not what you want.

expr is archaic. A more modern solution would use the shell's $((...)) form for arithmetic:

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

The shell only does integer arithmetic. If you actually wanted floating-point accurate results (which happen to be the same here), use bc:

$ echo "$str2" | bc -l
15.00000000000000000000

Note that $str2 is enclosed in double-quotes to prevent shell mischief.

John1024
  • 74,655
1

When you expand a variable outside of double quotes, as in expr $str2, the following things happen:

  1. Take the value of the variable. The result is a string.
  2. Split the value into whitespace-delimited chunks.¹ The result is a list of strings.
  3. Interpret each element of the list as a wildcard pattern, i.e. globbing. If it matches files, replace the element by the list of matches. If it doesn't match any file, leave the element alone.

For example, the value of your variable contains the word *, which is replaced by the list of file names in the current directory.

Generally speaking, always put double quotes around variable substitutions and command substitutions: "$stuff", "`stuff`". See Why does my shell script choke on whitespace or other special characters? Only leave out the quotes if you know and understand why you need to leave out the quotes.

In this case, you need to have step 2 (splitting) happening, because expr needs the operators and operands in separate arguments. But step 3 (treating each word as a filename wildcard pattern) must not happen. You can do this by turning off globbing:

result=`set -f; expr $str2`

In ksh or bash, use an array instead of a string — but in these shells you wouldn't have any use for expr.

Unless you need your script to be portable to ancient Bourne shells, you don't need to use expr. Arithmetic expressions are a standard POSIX feature.

result=$(($str2))

¹ The separator characters can be configured via the IFS variable.