expr(1) on Solnix vs Linux and the BSDs
On Solnix, expr is the illumos /usr/bin/expr. It has three variants that differ in output and in what the : operator counts. The most important difference for Linux and BSD users is that length, substr and index are documented only as x86-only compatibility operators, and there is no match keyword or + TOKEN escape.
The manual pages
- Solnix: expr(1) from
pkgs.solnix.illumos-world-man· illumos.org - Linux: expr(1) coreutils-full-9.11
- FreeBSD: expr(1) 15.1-RELEASE
- OpenBSD: expr(1) 8.0
- NetBSD: expr(1) 11.0
- Standard: POSIX.1-2024 (IEEE Std 1003.1-2024),
expr
What Solnix runs
A default Solnix system runs the illumos /usr/bin/expr. /usr/xpg4/bin/expr and /usr/xpg6/bin/expr are also shipped. /usr/bin/expr and xpg4 write nothing for a null string, while xpg6 always ends the output with a newline. The : operator counts bytes in /usr/bin/expr and characters in xpg4. /usr/ucb/expr is listed as shipped, but the bundle does not describe it.
Differences
| Compared with | Topic | What differs |
|---|---|---|
| all four | output for null result | Solnix /usr/bin/expr and /usr/xpg4/bin/expr write 0 for a zero value and nothing at all for a null string. /usr/xpg6/bin/expr always ends output with a newline, even with no result. The counterpart pages do not say what they print for a null string.Source: Solnix expr(1) DESCRIPTION (/usr/bin/expr, /usr/xpg4/bin/expr; /usr/xpg6/bin/expr); Linux/FreeBSD/OpenBSD/NetBSD expr(1) DESCRIPTION |
| all four | bytes vs characters for : | Solnix /usr/bin/expr returns the number of bytes matched by the : operator. Solnix /usr/xpg4/bin/expr returns characters. Linux and the three BSD pages say characters. In multibyte locales, /usr/bin/expr can give a different count.Source: Solnix expr(1) OPERANDS (expr : expr); Linux expr(1) (STRING : REGEXP); FreeBSD/OpenBSD/NetBSD expr(1) expr1 : expr2 |
| Linux | string keywords | Solnix documents index, length and substr only as 'Compatibility Operators (x86 only)', and they count bytes. There is no match keyword and no + TOKEN escape. Linux documents match, substr, index, length and + TOKEN as ordinary expressions. Source: Solnix expr(1) Compatibility Operators (x86 only); Linux expr(1) match, substr, index, length, + TOKEN |
| FreeBSD, OpenBSD, NetBSD | string keywords on the BSDs | FreeBSD treats length, substr, index and match as plain strings. OpenBSD documents no such keywords. NetBSD documents only length (bytes), which it calls a GNU-compatibility extension. Solnix documents index, length and substr, but only as x86 compatibility operators. Source: FreeBSD expr(1) STANDARDS; OpenBSD expr(1) DESCRIPTION; NetBSD expr(1) length, STANDARDS; Solnix expr(1) Compatibility Operators |
| Linux | exit status 3 | Solnix documents exit status >2 for any error. Linux documents exactly 3. A Linux script that tests for 3 explicitly should test for a value above 2 on Solnix. Source: Solnix expr(1) EXIT STATUS (>2); Linux expr(1) DESCRIPTION (exit status) |
| FreeBSD | exit status above 2 | FreeBSD documents only exit statuses 0, 1 and 2. Solnix, OpenBSD and NetBSD also document >2 for errors such as memory allocation failure. Overflow on FreeBSD ends the program with an error status, but the number is not given. Source: FreeBSD expr(1) EXIT STATUS, DESCRIPTION (overflow); Solnix expr(1) EXIT STATUS |
| FreeBSD | -e option, EXPR_COMPAT and leading minus | FreeBSD has -e and the EXPR_COMPAT variable, and treats a leading argument starting with a minus as an option unless compat mode is on. The Solnix page documents no options, no EXPR_COMPAT and no -- handling.Source: FreeBSD expr(1) SYNOPSIS, -e, ENVIRONMENT, Compatibility with previous implementations; Solnix expr(1) SYNOPSIS |
| Linux | options | Linux expr accepts --help and --version. The Solnix page documents no options, only argument operands.Source: Linux expr(1) SYNOPSIS, --help, --version; Solnix expr(1) SYNOPSIS |
| Linux, FreeBSD, NetBSD | operand that looks like an operator | On Solnix, a variable that expands to an operator such as = is read as the operator. The page's workaround is to prefix both sides with a letter: expr X$a = X=. Linux has the + TOKEN form. FreeBSD says such an operand is a syntax error. NetBSD says it handles / and -- by context. Source: Solnix expr(1) NOTES; Linux expr(1) + TOKEN; FreeBSD expr(1) DESCRIPTION; NetBSD expr(1) COMPATIBILITY |
| FreeBSD, NetBSD | integer range and overflow | FreeBSD uses intmax_t arithmetic and NetBSD uses 64-bit integers, and both check for overflow. The Solnix page does not state an integer range or overflow behaviour. Source: FreeBSD expr(1) DESCRIPTION; NetBSD expr(1) COMPATIBILITY; Solnix expr(1) OPERANDS |
| all four | expression length limit and blanks | The Solnix page says terms must be separated by blanks and the expression is limited to LINE_MAX (2048 characters). None of the counterpart pages state such a limit. Source: Solnix expr(1) OPERANDS; Linux/FreeBSD/OpenBSD/NetBSD expr(1) DESCRIPTION |
| FreeBSD, OpenBSD, NetBSD | failed match with a subexpression | The BSD pages say a failed match returns the null string when the pattern has a \(...\) subexpression, and 0 otherwise. The Solnix page says only that the operator returns 0 on failure, and \1 when a subexpression exists. Source: FreeBSD/OpenBSD/NetBSD expr(1) expr1 : expr2; Solnix expr(1) OPERANDS (expr : expr) |
Standards
- POSIX: expr is a POSIX.1-2024 utility.
- Solnix: Interface Stability: Standard. The ATTRIBUTES table also lists CSI as enabled.
- FreeBSD: The expr utility conforms to IEEE Std 1003.2008 (“POSIX.1”), provided that backwards compatibility mode is not enabled. The
-eflag is an extension. - OpenBSD: The expr utility is compliant with the IEEE Std 1003.1-2008 (“POSIX.1”) specification.
- NetBSD: The expr utility conforms to IEEE Std 1003.2 (“POSIX.2”). The length keyword is an extension for compatibility with GNU expr.
Writing portable scripts
- Use only the operators | & = > >= < <= != + - * / % and :, quoting those the shell treats specially. Avoid length, substr, index and match, which are x86-only on Solnix and undefined or missing on some BSDs.
- Guard against operands that look like operators, for example expr X$a = Xvalue. Do not rely on a leading minus or a bare -- as the first argument.
- Treat any exit status above 1 as failure instead of testing for 2 or 3, because Linux uses 3 and Solnix uses >2 for errors.
Every difference here is taken from the manual pages linked above: Solnix's published page, Linux (man-pages and the GNU and util-linux manuals), FreeBSD, OpenBSD and NetBSD. Each row names its source. Where a manual is silent, this page is too. Compiled 2026-10-26. Found a mistake? Mail hackers@lists.solnix.io.