Older POSIX-style shell scripts often reach for expr when they need a small calculation, comparison, or string check without Bash-specific syntax. The expr command in Linux handles integer math, string operations, comparisons, and anchored regular-expression matches, but its spacing, quoting, and exit-status rules are easy to misread.
Modern Bash usually has cleaner tools: arithmetic expansion for math, [[ ... ]] for conditions, parameter expansion for simple string work, and awk when text processing grows. Treat expr as a compatibility tool and a way to understand older shell scripts, not the default choice for every new expression.
Understand the expr Command in Linux
The GNU Coreutils expr documentation defines expr as an expression evaluator that writes the result to standard output. The important detail is that every operator and operand must arrive as its own argument after the shell has finished parsing quotes, globs, redirects, pipes, and parentheses.
The basic syntax is short:
expr EXPRESSION
In that form, EXPRESSION is not one quoted formula. It is a sequence of separate arguments such as 7, +, 5, or length, string. The GNU Coreutils manual lists GNU operators; when you need portable shell-script behavior, treat POSIX behavior as the safer baseline instead of relying on GNU extensions.
Check expr Availability and Version
Most general-purpose Linux systems provide expr through a Coreutils implementation. The version line often names GNU Coreutils, but some systems may use another implementation such as uutils Coreutils. Confirm the command and implementation before relying on extension behavior such as the + TOKEN string escape.
command -v expr
expr --version | head -n 1
On a GNU Coreutils system, typical output looks like this, with the version number changing by distribution:
/usr/bin/expr expr (GNU coreutils) 9.11
If the version line does not name GNU Coreutils, or if expr --version fails but command -v expr succeeds, verify any extension before using it in a portable script. The POSIX specification remains the safer reference when the target implementation is uncertain.
expr Command Quick Reference
| Task | Expression Pattern | Result or Use |
|---|---|---|
| Add integers | expr 7 + 5 | Prints 12. |
| Multiply safely | expr 7 '*' 5 | Quotes * so the shell does not expand it as a glob. |
| Group arithmetic | expr '(' 7 + 5 ')' '*' 2 | Prints 24 by grouping before multiplication. |
| Compare values | expr 10 '>' 7 | Prints 1 for true and 0 for false. |
| Find string length | expr length 'LinuxCapable' | Prints the character count. |
| Extract a substring | expr substr 'LinuxCapable' 6 7 | Uses 1-based positions and prints Capable. |
| Match an anchored pattern | expr 'release-2026' : 'release-[0-9][0-9][0-9][0-9]' | Prints the number of matching characters. |
| Capture a regex group | expr 'release-2026' : 'release-\([0-9][0-9][0-9][0-9]\)' | Prints the captured text, such as 2026. |
Practical expr Command Examples
Keep Operators and Operands Separate
expr does not parse 1+2 as arithmetic because the plus sign is not a separate argument. Put spaces around each operator so expr receives the operator as its own token.
expr 1+2
expr 1 + 2
1+2 3
The first command prints the literal string 1+2. The second command performs addition because 1, +, and 2 are separate arguments.
Calculate Arithmetic with expr
Use +, -, *, /, and % for integer arithmetic. Quote * and parentheses because the shell treats them as special characters before expr runs.
expr 7 + 5 '*' 2
expr '(' 7 + 5 ')' '*' 2
17 24
The first expression follows normal precedence, so multiplication happens before addition. The second expression groups 7 + 5 first, then multiplies the grouped value.
Division uses integer results, and the remainder operator reports what is left after integer division.
expr 17 / 5
expr 17 % 5
3 2
Use shell arithmetic such as $((17 / 5)) in new Bash scripts when you do not need expr compatibility. It avoids spawning an external command and has cleaner syntax for normal integer math.
Increment a Shell Variable with expr
Older scripts often use expr inside command substitution to update counters. Quote the variable so an empty or space-containing value does not split into extra arguments.
count=41
count=$(expr "$count" + 1)
printf '%s\n' "$count"
42
The printf command in Linux keeps script output predictable after the calculation. In Bash-only code, ((count++)) or count=$((count + 1)) is usually clearer.
Validate Numeric Input Before Arithmetic
When a script receives a value from an argument, prompt, or environment variable, check that the value is a positive integer before using arithmetic operators. Prefixing the value with X keeps a leading dash from being parsed as expr syntax, while [1-9][0-9]*$ requires a nonzero first digit followed by digits through the end of the value.
threads='8'
if expr "X$threads" : 'X[1-9][0-9]*$' >/dev/null; then
next_threads=$(expr "$threads" + 1)
printf 'next_threads=%s\n' "$next_threads"
else
printf 'threads must be a positive integer\n' >&2
fi
next_threads=9
This pattern is useful in POSIX-style scripts where [[ ... ]] is not available. In Bash-only scripts, a conditional such as [[ $threads =~ ^[1-9][0-9]*$ ]] is usually easier to read for the same positive-integer check.
Compare Numbers and Strings with expr
Comparison operators print 1 for true and 0 for false. Quote < and > so the shell does not treat them as redirection operators.
expr 10 '>' 7
expr 10 '<' 7
expr 'alpha' = 'alpha'
expr 'alpha' '!=' 'beta'
1 0 1 1
When both arguments look like integers, expr performs an arithmetic comparison. Otherwise, string comparisons follow the implementation’s locale and collation rules. For ordinary shell conditions and file checks, prefer Bash file-existence and conditional checks instead of forcing expr into that role.
Compare Numeric-Looking Strings as Text
Build numbers, fixed-width counters, and IDs can look numeric while still needing a text comparison. If both operands look like integers, expr may compare them numerically and treat 08 as equal to 8. Add the same non-numeric prefix to both sides when the exact text matters.
expr 08 = 8
expr X08 = X8
1 0
The first comparison evaluates the operands as numbers. The second comparison keeps the leading zero visible because neither operand is a bare integer anymore, so it prints 0 and exits with status 1 for a valid false result.
Find String Length, Substrings, and Character Positions
GNU expr and compatible Coreutils implementations support string helper keywords such as length, substr, and index. Positions are counted from 1, not 0.
expr length 'LinuxCapable'
expr substr 'LinuxCapable' 6 7
expr index 'LinuxCapable' 'Cz'
12 Capable 6
substr starts at character position 6 and returns 7 characters, which is why it prints Capable. index returns the first position where any character from the second string appears, so C is found at position 6 before z is considered.
Match Anchored Regular Expressions with expr
The colon operator matches a basic regular expression against the start of the string. Without a captured group, it returns the number of matching characters. With a captured group, it returns the captured text.
expr 'release-2026' : 'release-[0-9][0-9][0-9][0-9]'
expr 'release-2026' : 'release-\([0-9][0-9][0-9][0-9]\)'
12 2026
The pattern is anchored automatically, so it starts matching at the beginning of release-2026. The second command uses escaped basic-regular-expression parentheses to capture only the year. For file or stream searches, use grep command patterns instead of making expr process full lines one at a time.
Extract a Version Field in a Script
Anchored capture is useful when an old shell script needs one field from a predictable string. This example extracts the major version from a package-like value.
release='nginx-1.28.0'
major=$(expr "$release" : 'nginx-\([0-9][0-9]*\)')
printf 'major=%s\n' "$major"
major=1
The pattern requires nginx- at the start, then captures one or more digits. For larger record-oriented parsing, awk command examples scale better because awk can read fields, loop over input, and print formatted reports in one process.
Use expr Exit Status Correctly
expr prints a result and also returns a status code. A true, nonzero, non-empty result exits with status 0. A valid expression that evaluates to 0 or an empty string exits with status 1. A syntax error exits with status 2, and internal errors use a status greater than 2.
That means a false comparison is not the same as a broken command:
expr 10 '<' 7
printf 'status=%s\n' "$?"
0 status=1
The expression is valid, but it is false. In a script, use an if statement when you want the status to choose a branch, and redirect output when the printed 1 or 0 is not needed.
if expr 10 '<' 7 >/dev/null; then
printf 'true\n'
else
printf 'false\n'
fi
false
Avoid placing false-result expr checks inside brittle set -e flows unless you handle status 1 deliberately. The nonzero status is meaningful, not necessarily a failure.
Handle expr Quoting and Ambiguous Tokens
Quote Shell Operators Before expr Sees Them
The shell interprets characters such as *, (, ), <, >, |, and & before starting expr. Quote or escape those tokens when they are meant to be expr operators.
expr '(' 2 + 3 ')' '*' 4
20
Single quotes are often easier to read than backslashes because the visible token stays unchanged. The same idea applies to comparisons such as expr 10 '>' 7 and expr 10 '<' 7.
Protect Leading-Dash Values
Coreutils-style expr implementations accept option tokens such as --help and --version, and a first operand beginning with - can still be confused with option parsing. Put -- before operands when the first value may be negative or may start with a dash.
expr -- -5 + 2
-3
This option-terminator pattern matters most when a script receives values from variables rather than from hardcoded examples. For POSIX-oriented scripts, parenthesize the first value instead of relying on --.
expr '(' -5 ')' + 2
-3
Parentheses are still expr syntax, so quote them before the shell can treat them as grouping operators.
Handle Strings That Look Like expr Operators
Quoting protects values from the shell, but it does not stop expr from interpreting a token such as match, /, or = as part of its own expression language.
token='match'
LC_ALL=C expr "$token" = match
expr: syntax error: missing argument after 'match'
GNU expr and compatible Coreutils implementations support + TOKEN to force the next token to be treated as a string:
token='match'
expr + "$token" = + match
1
For portable scripts, a common workaround is to prefix both sides with a harmless character and compare the prefixed values:
value='='
expr "X$value" = 'X='
1
If a script accepts arbitrary user-provided strings, this ambiguity is a strong reason to use test, [ ... ], [[ ... ]], or another purpose-built tool instead of stretching expr.
Choose expr or a Better Shell Tool
expr is useful, but it should not be the automatic answer for every expression in a modern shell script. Choose the tool that matches the task and portability requirement.
| Task | Prefer | Reason |
|---|---|---|
| Maintain older POSIX-style scripts | expr | Works in legacy scripts and small shell environments where newer Bash syntax may not be available. |
| Integer math in Bash | $(( ... )) | Built into the shell, easier to read, and avoids an external process. |
| File and directory checks | test, [ ... ], or [[ ... ]] | Purpose-built for conditions; use Bash file-existence checks when path type matters. |
| Simple Bash string length or slicing | ${#var} and parameter expansion | Avoids expr token ambiguity in Bash-only scripts. |
| Formatted terminal output | printf | Gives predictable newlines, padding, and field formatting. |
| Text records, fields, and reports | awk | Handles line-by-line input, fields, loops, math, and formatted output in one tool. |
| Search files or streams with regex | grep | Designed for matching lines from files, logs, command output, and pipelines. |
That split keeps expr where it is strongest: small compatibility expressions and predictable examples from older shell scripts. When the expression becomes a mini-program, move to the command that owns the workflow.
Troubleshoot Common expr Errors
Fix Missing Operand Syntax Errors
This error usually means an operator is missing an operand, or the shell changed the argument list before expr received it. Set LC_ALL=C for stable English diagnostics when you are capturing or comparing error text.
LC_ALL=C expr 1 +
expr: syntax error: missing argument after '+'
Add the missing operand and keep the operator separated by spaces:
expr 1 + 2
3
Fix a False Result That Looks Like a Command Failure
A false comparison prints 0 and exits with status 1. That status is meaningful, not a syntax error.
expr 10 '<' 7
printf 'status=%s\n' "$?"
0 status=1
Handle that status with an explicit branch when the script should continue after a false result:
if expr 10 '<' 7 >/dev/null; then
printf 'true\n'
else
printf 'false\n'
fi
false
Fix Non-Integer Argument Errors
The expr: non-integer argument error means at least one arithmetic operand is empty or contains non-numeric text. Print the variable in brackets before the calculation so whitespace and empty values are visible.
count=''
printf 'count=[%s]\n' "$count"
LC_ALL=C expr "$count" + 1
count=[] expr: non-integer argument
Guard the value before arithmetic when the script should reject empty or non-numeric input.
count='8'
if expr "X$count" : 'X[0-9][0-9]*$' >/dev/null; then
count=$(expr "$count" + 1)
printf 'count=%s\n' "$count"
else
printf 'count must contain digits only\n' >&2
fi
count=9
Fix Shell Errors Around *, Parentheses, or Redirection
If arithmetic examples fail near *, (, ), <, or >, the shell probably interpreted the token before expr started. Quote each operator token that has shell meaning.
expr '(' 2 + 3 ')' '*' 4
20
Use the same approach for comparisons: write expr 10 '>' 7 or expr 10 '<' 7, not an unquoted redirection token.
Fix Strings That Are Parsed as expr Syntax
When a variable can contain a token such as match, =, /, or a leading dash, shell quoting alone is not enough. Reproduce the parse problem with a small value before changing a larger script.
token='match'
LC_ALL=C expr "$token" = match
expr: syntax error: missing argument after 'match'
Use the string-token escape only when the target implementation supports it:
token='match'
expr + "$token" = + match
1
For portable scripts, use the prefix workaround so the compared values no longer look like raw expr syntax:
value='='
expr "X$value" = 'X='
1
When arbitrary strings are normal input, shell conditionals or another purpose-built comparison tool are usually clearer than stretching expr.
Conclusion
expr fits best as a compatibility tool for legacy-friendly integer math, comparisons, string slices, and anchored regex checks. Keep spacing, quoting, and exit status deliberate; for new scripts, move file checks to Bash conditionals, formatted output to printf, and record-based text processing to awk when the expression starts growing.


Formatting tips for your comment
You can use basic HTML to format your comment. Useful tags currently allowed in published comments:
<code>command</code>command<strong>bold</strong><em>italic</em><a href="https://example.com">link</a><blockquote>quote</blockquote>