expr Command in Linux: Math, Strings, Regex, and Exit Status

Older shell scripts still use expr for small calculations and string checks; trace its spacing, quoting, regex, and exit-status rules before choosing Bash arithmetic, awk, grep, or printf instead.

PublishedAuthorJoshua JamesRead time8 minGuide typeLinux Commands

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

TaskExpression PatternResult or Use
Add integersexpr 7 + 5Prints 12.
Multiply safelyexpr 7 '*' 5Quotes * so the shell does not expand it as a glob.
Group arithmeticexpr '(' 7 + 5 ')' '*' 2Prints 24 by grouping before multiplication.
Compare valuesexpr 10 '>' 7Prints 1 for true and 0 for false.
Find string lengthexpr length 'LinuxCapable'Prints the character count.
Extract a substringexpr substr 'LinuxCapable' 6 7Uses 1-based positions and prints Capable.
Match an anchored patternexpr 'release-2026' : 'release-[0-9][0-9][0-9][0-9]'Prints the number of matching characters.
Capture a regex groupexpr '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.

TaskPreferReason
Maintain older POSIX-style scriptsexprWorks 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 checkstest, [ ... ], or [[ ... ]]Purpose-built for conditions; use Bash file-existence checks when path type matters.
Simple Bash string length or slicing${#var} and parameter expansionAvoids expr token ambiguity in Bash-only scripts.
Formatted terminal outputprintfGives predictable newlines, padding, and field formatting.
Text records, fields, and reportsawkHandles line-by-line input, fields, loops, math, and formatted output in one tool.
Search files or streams with regexgrepDesigned 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.

Share this guide

Help another Linux user troubleshoot faster

Share this guide with someone troubleshooting Linux systems or saving it for later.

Follow LinuxCapable

Want more LinuxCapable guides in Google?

Add LinuxCapable as a preferred source so Google can show more of our fresh Linux tutorials in Top Stories and From your sources when relevant.

Add LinuxCapable as a preferred source on Google
Search LinuxCapable

Need another guide?

Search LinuxCapable for package installs, commands, troubleshooting, and follow-up guides related to what you just read.

Found this guide useful?

Support LinuxCapable to keep tutorials free and up to date.

Buy me a coffeeBuy me a coffee
Before commenting, please review our Comments Policy.
Formatting tips for your comment

You can use basic HTML to format your comment. Useful tags currently allowed in published comments:

You type Result
<code>command</code> command
<strong>bold</strong> bold
<em>italic</em> italic
<a href="https://example.com">link</a> link
<blockquote>quote</blockquote> quote block

Got a Question or Feedback?

We read and reply to every comment - let us know how we can help or improve this guide.

Verify before posting: