Home / Alt manpages / expr(1)

  • expr(1)
  • User command
  • linux

Use expr Safely for Shell Arithmetic and String Tests

You will finish with a small set of verified expr commands for arithmetic, comparisons, regular-expression matches and string operations. The examples target GNU coreutils 9.4, which is the version installed on this machine.

Allow about fifteen minutes. You need a POSIX-style shell and the coreutils package. All commands here run as an ordinary user. Nothing changes on disk, and no elevated privileges are needed.

1. Check the installed command

Confirm which executable is being used and record its version before relying on details in a script:

$ command -v expr
/usr/bin/expr
$ expr --version | head -n 1
expr (GNU coreutils) 9.4

The GNU manual describes expr as an expression evaluator. It accepts one expression as separate shell arguments and prints the resulting value. The shell parses the command line first, so quoting and escaping are part of using the tool correctly.

Checkpoint

If command -v expr returns a different path, repeat the version check and use that implementation's manual. Do not assume that another expr has exactly the same diagnostics or extensions.

2. Evaluate integer arithmetic

Pass numbers and operators as separate arguments. Addition, subtraction, multiplication, division and remainder are available:

$ expr 9 + 4
13
$ expr 18 - 7
11
$ expr 6 \* 5
30
$ expr 17 / 5
3
$ expr 17 % 5
2

The multiplication operator must be protected from the shell. An unquoted * can expand to file names in the current directory before expr sees it. A backslash or single quotes prevent that expansion. Division is integer division, so 17 / 5 produces 3, not a decimal value.

For values supplied by variables, quote the expansions and keep the operator separate:

$ left=12
$ right=8
$ expr "$left" + "$right"
20

These examples assume trusted integer input. expr is not an input validator. Check data before evaluating it if a value comes from a file, user input or another process.

3. Compare numbers and strings

Use the comparison operators as separate arguments. If both operands look like numbers, GNU expr compares them arithmetically. Otherwise it compares them lexicographically:

$ expr 12 '>' 3
1
$ expr 12 '=' 12
1
$ expr apple '<' banana
1
$ expr apple '=' apple
1
$ expr apple '=' orange
0

The result is normally 1 for true and 0 for false. Quote or escape operators such as <, > and & so the shell does not treat them as redirection or control syntax. Single quotes make the intended argument visible in a script.

The logical operators | and & use expr's notion of a false value: an empty string or numeric zero. For example:

$ expr 0 '|' 12
12
$ expr 12 '&' 5
12
$ expr 0 '&' 5
0

Use \| and \& in the shell command above because the backslash prevents shell interpretation. The operator returns one of its operands, not necessarily a Boolean word. If you need a simple condition, use the command's exit status and make the expression's false case explicit.

4. Use the exit status in a shell condition

expr exits with status 0 when its result is neither empty nor zero, and status 1 when its result is empty or zero. Status 2 means the expression is syntactically invalid; status 3 means another error occurred. This makes a comparison usable directly in an if statement:

$ version=12
$ if expr "$version" '>' 10 > /dev/null; then
>     printf '%s\n' 'version is above the limit'
> fi
version is above the limit
$ printf 'status: %s\n' "$?"
status: 0

Redirecting standard output keeps the comparison result out of the user-facing output. Capture the status immediately if you need it. A later command replaces $?. Do not use expr output as a Boolean string without checking this distinction.

To inspect a failure without changing anything, run a deliberately false comparison and print its status:

$ expr 2 '>' 9 > /dev/null
$ printf 'status: %s\n' "$?"
status: 1

5. Extract text with match and substr

The length, index and substr expressions handle basic string work. Positions are counted from 1:

$ expr length 'server-42'
9
$ expr index 'server-42' '-'
7
$ expr substr 'server-42' 1 6
server

index returns the position of the first character in its second argument that appears in the first string. It returns 0 when none is found:

$ expr index 'server-42' 'xyz'
0

For a regular-expression match, use either STRING : REGEXP or the equivalent match STRING REGEXP form. The expression is anchored at the start of the string:

$ expr 'alphabet' : 'alpha\(.*\)'
bet
$ expr 'alphabet' : 'beta'
0

When the pattern contains \( and \), the result is the text captured between them. Without a capture, the result is the number of characters matched. A failed match returns 0, which also gives the command a false exit status.

6. Avoid shell and expression traps

Operators are not interchangeable with shell syntax. A pipe, ampersand, less-than sign, greater-than sign, asterisk and parentheses can all have a meaning before expr runs. Quote them or escape them. Parenthesised expressions need shell protection as well:

$ expr '(' 2 + 3 ')' \* 4
20

Use + before a token that would otherwise be read as an operator or keyword. For example, this asks for the literal string match:

$ expr + match
match

Do not confuse a zero result with a command failure. For example, a missing character from index is an ordinary result with status 1, while malformed syntax has status 2:

$ expr 2 +
expr: syntax error: missing argument after '+'
$ printf 'status: %s\n' "$?"
status: 2

The wording of diagnostics can vary slightly with the installed build. The status classes and printed result are the useful parts to test in a script.

Done means

  • You checked that the expected GNU expr executable and version are installed.
  • You quote or escape shell operators before they reach expr.
  • You know that arithmetic division is integer division.
  • You can distinguish a false result, invalid syntax and an execution error by exit status.
  • You can use length, index, substr and anchored regular-expression matches for small string tasks.
  • You validate untrusted input instead of treating expr as an input parser.