% Shell integer arithmetic: expr, let, $((...)) % Ian! D. Allen -- -- [www.idallen.com] % Winter 2018 - January to April 2018 - Updated 2018-10-26 09:49 EDT - [Course Home Page] - [Course Outline] - [All Weeks] - [Plain Text] Shell integer arithmetic ======================== The shell is not a normal programming language and it does not do arithmetic or mathematics well. In particular, the shell only accepts integer numbers -- no floating-point (real, decimal) numbers. Even integer arithmetic was not built-in to the original shells. To do arithmetic, the shells once used "helper" commands. Modern shells now have the arithmetic built-in to the shell, but many, many older shell scripts still use the old helper commands. There are several historic ways to use helper commands to allow the shell to do integer arithmetic. > Shells don't do math on floating-point (real) numbers (numbers with decimal > points, e.g. `3.7`). If you want to do floating-point arithmetic, you > really do need to use a helper program such as `dc` or `bc`. The universal `expr` arithmetic helper command ---------------------------------------------- The oldest and most universal arithmetic helper command is the `expr` command, and it is often used in older scripts inside the historic back-quote form of Command Substitution to capture its output value: $ expr 2 + 2 * 9 20 $ x=`expr 2 + 2 * 9` # historic `...` command substitution syntax $ echo "$x" 20 The individual arguments to the `expr` command constitute the arithmetic expression to be evaluated and the value is printed on standard output. Shell meta-characters such as `*` (GLOB) must be quoted to hide them from the shell. All numbers and operators must be individual arguments, separated by blanks, otherwise `expr` simply echoes the unrecognized expression back to the user without any error message and without doing any mathematics: $ expr 2 + 2 4 $ expr 2+2 2+2 The `expr` command is the original shell arithmetic helper command. It is standard and works in all Unix/Linux shells ever written. Because `expr` is often an external command in many shells, not a shell built-in, it is slow. Using it in a loop that has to iterate hundreds or thousands of times will not be fast. You can read more in the manual page `expr(1)`. Don't use the legacy `expr` command in modern shell scripts. The occasional `let` built-in arithmetic helper command ------------------------------------------------------- Some later Bourne shells invented and used the `let` built-in helper command to do arithmetic: $ let x=4 y=5 z=x+y $ echo "$x $y $z" 4 5 9 - Each argument is an arithmetic expression to evaluate - Shell variables are allowed with or without leading `$` - Arithmetic expressions can involve assignment of values to variables, where `=` is the basic assignment operator - See the **ARITHMETIC EVALUATION** section of the `bash` man page The `let` helper command is not universally available in all Bourne shells. (In particular, the `dash` shell used as `/bin/sh` under Ubuntu does not support it!) Don't use it in new scripts. You can read more in the BASH shell help page `help let`. Don't use the legacy `let` command in modern shell scripts. The modern `$((...))` syntax for Arithmetic Expansion ----------------------------------------------------- The modern way to do arithmetic that we will use in this course does away with all the old helper commands. Arithmetic expressions are done directly by the shell using a special dollar-and-double-parenthesis **Arithmetic Expansion** syntax: `$((`*expression*`))` The enclosed *expression* is designed for arithmetic and is *not* GLOB expanded. Arguments do not need surrounding spaces or special quoting and variables do not need leading `$`: $ echo $(( 2 + 2 * 9 )) 20 $ echo $((2+2*9)) 20 $ x=2 y=3 ; echo $((x*y)) 6 - The `$((`*expression*`))`is an Arithmetic Expansion, like a variable expansion, and it can be used anywhere that you might use a variable. - Just as with variable substitution, the `$((`*expression*`))`is is replaced by the results of the evaluated Arithmetic Expression Here is a small example shell script using basic arithmetic: #!/bin/sh -u linkcount=$( ls -ld "$1" | awk '{print $2}' ) subdir=$(( linkcount - 2 )) echo "Directory $1 has link count $linkcount and $subdir sub-directories" Running the above script: $ ./example.sh /etc Directory /etc has link count 183 and 181 sub-directories $ ./example.sh / Directory / has link count 25 and 23 sub-directories The same script could be written as a single line, but it would have to execute the `ls` pipeline twice and would be be much harder to read (remember blanks are optional inside the *expression*): #!/bin/sh -u echo "Directory $1 has link count $(ls -ld "$1"|awk '{print $2}') and $(($(ls -ld "$1"|awk '{print $2}')-2)) sub-directories" Yes, that line contains a Command Substitution *inside* an Arithmetic Expression. Don't write hard-to-read code like this! Write the script on separate (shorter) lines so you can read it. Legacy `$[...]` syntax for Arithmetic Expansion ----------------------------------------------- Some older versions of the Bourne shells use the old syntax `$[`*expression*`]` for Arithmetic Expansion, but this is now obsolete and deprecated: $ echo $[2+2*9] # deprecated; do not use this syntax 20 Don't use the legacy `$[...]` syntax in modern shell scripts. -- | Ian! D. Allen, BA, MMath - idallen@idallen.ca - Ottawa, Ontario, Canada | Home Page: http://idallen.com/ Contact Improv: http://contactimprov.ca/ | College professor (Free/Libre GNU+Linux) at: http://teaching.idallen.com/ | Defend digital freedom: http://eff.org/ and have fun: http://fools.ca/ [Plain Text] - plain text version of this page in [Pandoc Markdown] format [www.idallen.com]: http://www.idallen.com/ [Course Home Page]: .. [Course Outline]: course_outline.pdf [All Weeks]: indexcgi.cgi [Plain Text]: 715_shell_arithmetic.txt [Pandoc Markdown]: http://johnmacfarlane.net/pandoc/