Text processing
expr
Evaluate an integer, comparison or string expression given as separate arguments and print the result.
expr EXPRESSION
expr is how Bourne shell scripts did arithmetic and string matching before shells had it
built in. Each operand and operator must be a separate argument, so spaces around operators
are mandatory, and operators the shell treats specially (*, <, >, |, &, (, ))
must be escaped or quoted.
In new scripts, prefer the shell's own tools: $(( )) for arithmetic, ${#var} for length,
[[ $s =~ re ]] or case for matching. They are faster (no new process) and need no
escaping, and $(( )) is POSIX, so it works in sh too. You still meet expr in old
scripts and installers, and its : match operator remains a compact way to extract part of a
string in plain sh.
The exit status is the trap: expr exits with 1 when the result is 0 or empty, even though
nothing went wrong, and 2 for a real error. Under set -e, i=$(expr $i - 1) reaching zero
ends the script.
Examples
Do integer arithmetic
expr 7 + 5
expr 7 / 2
expr 7 % 2
12 3 1
Integer math only: 7 / 2 truncates to 3 and % gives the remainder.
Spaces between arguments are required
expr 7+5
7+5
Without spaces, 7+5 is one argument, a string, and expr just prints it back.
Escape the multiplication sign
expr 6 \* 7
expr 6 '*' 7
42 42
An unescaped * would be expanded by the shell into the file names in the current
directory. A backslash or quotes pass it through to expr.
A result of zero sets exit status 1
expr 5 - 5; echo "exit status: $?"
expr 5 - 4; echo "exit status: $?"
0 exit status: 1 1 exit status: 0
expr prints 0 and returns 1, which a script with set -e treats as failure. A non-zero,
non-empty result returns 0.
Compare numbers and strings
expr 10 \> 9
expr 10 \> 9a
expr abc \< abd
1 0 1
Comparisons print 1 for true and 0 for false. When both sides are integers they compare
numerically; otherwise as strings, so 10 is not greater than 9a (1 sorts before
9).
Match the start of a string with the colon operator
expr 'backup-20260314.tar.gz' : 'backup-'
expr 'backup-20260314.tar.gz' : '[a-z]*'
7 6
STRING : REGEX anchors the basic regex at the start of the string and prints how many
characters matched: 7 for backup-, 6 for the run of letters.
Extract part of a string with a group
file='backup-20260314.tar.gz'
expr "$file" : 'backup-\([0-9]*\)'
expr "$file" : '.*\.\(.*\)'
20260314 gz
With a \( \) group, expr prints the captured text instead of a count: the date, then
the last extension. .*\. is greedy, so it runs to the last dot. In bash,
${file##*.} does the second one without a process.
String length, substring and index
expr length 'hello world'
expr substr 'hello world' 7 5
expr index 'hello world' 'ow'
11 world 5
These keywords are GNU extensions. substr counts from 1, and index returns the
position of the first character found from the set ow, which is the o at position 5.
Old-style loop counter
i=0
while [ "$i" -lt 3 ]; do i=$(expr "$i" + 1); echo "pass $i"; done
j=0; while [ "$j" -lt 3 ]; do j=$((j + 1)); echo "modern $j"; done
pass 1 pass 2 pass 3 modern 1 modern 2 modern 3
Both loops count to 3 under sh. The first starts a new expr process per iteration; the
second uses POSIX shell arithmetic, which is built in and the one to write today.
Non-numeric arguments are an error
expr 5 + five
expr: non-integer argument
An operand that is not an integer in an arithmetic expression gives an error message and exit status 2, which is how you tell a real error from a zero result.
Common pitfalls
Exit status 1 for a result of
0or an empty string breaksset -escripts andif expr ...tests. Check the output, not the status, or use$(( )).Unescaped
*,>,<,(and|are interpreted by the shell first:expr 3 > 2creates a file named2.If a string operand happens to look like an operator or keyword (
length,+,match), expr misparses the expression. GNU expr accepts+ STRINGto force it to be read as a string.expr runs a separate process for every call. In loops, shell arithmetic is orders of magnitude faster.
GNU vs BSD and macOS
The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).
length,substr,index,matchand the+quoting token are GNU extensions; BSD/macOS expr supports only the POSIX operators. Useexpr "$s" : '.*'for a portable length.
Options worth knowing
A selection, described in our own words. For the full list, see expr(1) or expr --help.
| Option | What it does |
|---|---|
--help | Show usage and exit. |
--version | Show version information and exit. |
Related
bcAn arbitrary-precision calculator language, used from scripts for decimal math the shell cannot do.testEvaluate a condition about files, strings or integers and return 0 for true or 1 for false.printfPrint text and values through a format string, with precise control over spacing, numbers and escapes.- ArithmeticInteger math with $(( )) and (( )), the exit status of (( )) that breaks set -e, leading-zero octal surprises, and what to use for decimals.
- String manipulationLength, substrings, prefix and suffix removal, search and replace, case, trimming, splitting and joining, done with bash's own expansions instead of external tools.
- Exit codesWhat exit statuses mean, how $? works, the reserved values 126, 127 and 128+N, PIPESTATUS, and how to choose codes for your own scripts.
- POSIX sh vs bashThe bashisms that break when a script runs under dash or another POSIX sh, the exact errors dash prints, and the portable replacement for each.