Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
YouTube Security VideosfreeCodeCamp.org: TimescaleDB Course – PostgreSQL for Time-Series Data(23.09.2026 um 12:30 Uhr)
Windows Tipps & SecurityAndroid 17: Rollout auf Samsung-Galaxy-Smartphones verzögert sich(23.09.2026 um 11:42 Uhr)
Unix & Linux ServerUSN-8733-2: Gzip vulnerabilities(22.09.2026 um 18:04 Uhr)
Sichere ProgrammierungHow to Build Custom PowerPoint Add-Ins for Enterprise Teams(23.09.2026 um 11:25 Uhr)
Sichere ProgrammierungSearch Google Jobs in Real-Time with Go and SerpApi 🚀(23.09.2026 um 12:13 Uhr)
Sichere ProgrammierungA Psychological State is a Coefficient Vector(23.09.2026 um 12:16 Uhr)
YouTube Security VideosfreeCodeCamp.org: TimescaleDB Course – PostgreSQL for Time-Series Data(23.09.2026 um 12:30 Uhr)
Windows Tipps & SecurityAndroid 17: Rollout auf Samsung-Galaxy-Smartphones verzögert sich(23.09.2026 um 11:42 Uhr)
Unix & Linux ServerUSN-8733-2: Gzip vulnerabilities(22.09.2026 um 18:04 Uhr)
Sichere ProgrammierungHow to Build Custom PowerPoint Add-Ins for Enterprise Teams(23.09.2026 um 11:25 Uhr)
Sichere ProgrammierungSearch Google Jobs in Real-Time with Go and SerpApi 🚀(23.09.2026 um 12:13 Uhr)
Sichere ProgrammierungA Psychological State is a Coefficient Vector(23.09.2026 um 12:16 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

HPR4707: UNIX Curio #12 - expr

This show has been flagged as Clean by the host. This series is dedicated to exploring little-known—and occasionally useful—trinkets lurking in the dusty corners of UNIX-like operating systems. Arithmetic is something that one would no…

0
↗ Quelle (hackerpublicradio.org)
Reagiere als Erste:r — dein Feedback zählt!

This show has been flagged as Clean by the host.




This series is dedicated to exploring little-known—and occasionally useful—trinkets lurking in the dusty corners of UNIX-like operating systems.



Arithmetic is something that one would normally expect computers to be able to do. With UNIX, one could of course always write a program to perform a calculation, but for people like me who are bad at programming, it would be nice to have a tool that makes things a bit easier.




First Edition UNIX in 1971 included such a tool, called


dc





, which stands for "desk calculator"



1

. This utility performed integer arithmetic (though later versions can handle real numbers) using reverse Polish notation. To divide 11 by 4 with this method, instead of entering "11 ÷ 4 =", you would key in "11 ENTER 4 ENTER ÷". The very first calculator I ever used, one made by Hewlett-Packard that my father brought home from work a few times, employed reverse Polish notation but I have never gotten used to it. All the calculators I have made significant use of and bought for myself used the more common infix notation "11 ÷ 4"—I also prefer computer utilities following that pattern, so I almost never use
dc

.




For reasons explained in the
rationale for the





bc





utility



2

,
dc

has not been standardized in POSIX despite its long tenure. However, while it doesn't seem to get a lot of attention, I still would not consider
dc

to be a UNIX Curio.




Today, it is possible to do integer arithmetic in a standard POSIX shell without any outside utilities. This is called "arithmetic expansion" and is described in references or manual pages for many shells. It wasn't always this way, however. Before a shell was available that supported arithmetic expansion, you needed to call another utility for your calculations, and


expr





was one of those



3

. That program is the UNIX Curio for this episode.




Some people might pronounce this name, but I find it awkward to say, so I just spell out
expr

the same as I would do with
dc

. Its name is an abbreviation of "expression", and it takes arguments representing an expression. An expression is formed by combining integers or strings with zero or more operator symbols. There is quite a variety of operators—some are mathematical, some perform comparisons, one matches a regular expression, and others are used for grouping or logical tests.




Since we started this episode talking about arithmetic, let's tackle those first. The "+", "-", "*", and "/" symbols are for performing addition, subtraction, multiplication, and division respectively, as is common in many programming and scripting languages. The "%" produces the remainder of integer division. So,
expr 11 / 4

would output
2

; it only does integer calculations. The command
expr 11 % 4

would output
3

—in this case, the remainder left after "4" is removed from "11" twice. Take note that
expr

expects the integers or strings and operators it is given to all be separate arguments. Running
expr 11/4

would just output the string
11/4

because the slash is not treated as being an operator. You also need to be careful with characters that have meaning to the shell—
expr 11 * 4

would probably result in an error because the shell will expand the asterisk to a list of files in the current directory. You would have to use
expr 11 \* 4

or
expr 11 "*" 4

instead to multiply those numbers.




It is possible for each integer to be preceded by a hyphen (with no spaces in between), meaning the number is negative. However, you need to be careful here, too. The command
expr $a + 1

could potentially fail if the value of
$a

is
-1

—some implementations might treat the
-1

as being an option to
expr

. (While POSIX does not specify any options, an implementation of the utility could add them as an extension.) The safer method is to make sure the variable doesn't appear first:
expr 1 + $a

would work, as would
expr \( $a \) + 1

. Parentheses can be used for grouping; in this example, they just prevent the value of
$a

from being the first argument. Another way to prevent a value from being treated as an option is to put the standard two hyphens after
expr

, signaling the end of options (for example,
expr -- $a + 1

).




The result of evaluating the expression is printed to standard output. The exit status of
expr

is also set based on the result—if the expression is evaluated successfully and the result is
not

zero or the null string, the exit status will be 0. The exit status is 1 if evaluation is successful and results in either zero or the null string. When the expression is invalid, the exit status will be 2 and if some other error occurs, it will be greater than 2. A script using
expr

can therefore potentially take some action depending on its exit status, the value it outputs, or both.




The next set of operators recognized by
expr

consists of comparison operators. These include "=", "!=", ">", ">=", "<", and "<=", and they work just how you would expect with integers. If one or both of the arguments are strings, however, instead of doing a numerical comparison, the arguments are compared using the collation sequence in the current locale. When the comparison is true,
expr

outputs "1" and returns an exit status of 0; if false, it outputs "0" and returns a status of 1. It is important to be careful in the arguments you use;
10 = 10.0

would evaluate to false because the period forces a string comparison rather than a numerical one. By contrast,
10 = 010

would evaluate to true; unlike some other utilities,
expr

does not consider a leading zero to mean an octal number. All numbers are treated as decimal and standard
expr

has no method for working with other bases.




There are also logical operators, though they might not act exactly the way you would expect if you are used to other programming languages. When a
&

symbol appears between two expressions, it outputs the result of the first one if both expressions evaluate to something that is not 0 or a null string. Otherwise, it outputs 0. If a
|

symbol appears between two expressions, it outputs the result of the first one provided it evaluates to something that is not 0 or a null string. Otherwise, it outputs the evaluation of the second expression if that is not a null string. If neither of these are true, it outputs 0. As with some other operators, these are significant to the shell so they need to be quoted.




I mentioned parentheses before; these can be used for grouping. The last operator I will cover is
:

and it is quite different from the others. Instead of performing a mathematical or logical function, it instead performs a regular expression match. The expression before the colon is treated as the string to match against, and the expression after the colon is a basic regular expression. (Be careful, features of extended regular expressions are
not

available.) There is one special characteristic to the match—it must occur starting at the beginning of the string, as if
^

appeared at the beginning of the regular expression. The colon operator normally returns the number of characters matched by the regular expression. So, for example,
expr "x$a" : ".*" - 1

would return the length of the string in
$a

. (The "x" is used in case
$a

happens to be a null string. Also,
${#a}

is a more efficient way to return the length of
$a

within modern shells.) However, if the regular expression contains any
subexpressions

, indicated by
\(
subexpression

\)

, this operator instead returns what is matched by the first subexpression, or a null string if that does not match. So in some cases,
expr

could be used in place of another tool like
grep

,
sed

, or
awk

.




There is one final case: where an integer or string appears without any operators at all. In that situation, it is simply output as a string with no calculations, comparisons, or matching performed. It's as if you used
echo

with a single argument.




So why would you want to use
expr

? For mathematical calculations, arithmetic expansion will certainly be faster because it happens within the shell. While
expr

does give you an exit status, allowing it to be used within shell constructs like
if

and
while

, it is likely that combining arithmetic expansion with the
test

utility (which is built in to many shells) would use fewer resources. However, arithmetic expansion has limits: POSIX only requires it to operate on signed long integers; what this means in practice depends on the platform you are running on. In contrast, the standard for
expr

does not say
anything

about what range of integers must be supported.
GNU's version uses arbitrary precision



4

, allowing it to represent very huge integers like
bc

does. The implementations of
expr

included with FreeBSD 15.0, NetBSD 10.1, and OpenIndiana 2025.10 all use a 64-bit signed representation (at least on the virtual machine I tested). While on FreeBSD and NetBSD an error results when the limit it can faithfully represent is exceeded, the OpenIndiana version just overflows and returns an incorrect value. So one cannot depend on
expr

providing any extra capabilities than shell arithmetic expansion unless the exact implementation is known.




If
expr

can't be relied on for any improvements in arithmetic relative to other tools, what about comparisons? For numerical comparisons,
test

offers the same set of options that
expr

does. When it comes to strings,
the POSIX standard only added





<





and





>





operators to the





test





utility in 2024



5


earlier versions just offered





=





and





!=





6

, so
expr

guaranteed more functionality up until just recently. There
is

a semantic difference between how
=

and
!=

in the two utilities behave with strings. In
test

, those operators mean "is (or is not) identical", while in
expr

they mean "collates (or does not collate) the same in the current locale". If this is important to you, there is no way to get the other behavior under standard POSIX with either of these utilities, so you have to choose the right one. I have to say that it is unclear to me how "collates the same" is applied in practice; some examples I tried did not reflect what I expected.






My reading of the
Unicode


standards

would imply that the presence of a ZERO WIDTH SPACE character (Unicode code point U+200B, represented in octal escape sequences as \342\200\213) does not affect how a string collates. However,





expr





seems

not
to ignore it. The below was obtained on Debian 12 using the en_US.UTF-8 (US English) locale, but I got the same results on FreeBSD 15.0. Reminder: in the output of





expr





, 1 means true and 0 means false.





$ printf 'abcd\n'
abcd
$ printf 'ab\342\200\213cd\n'
ab​cd
$ expr "abcd" = "$(printf 'ab\342\200\213cd')"
0
$ expr "abcd" \> "$(printf 'ab\342\200\213cd')"
1







Another section

of the same Unicode reference outright states that the three code points U+212B, U+00C5, and A followed by U+030A are equivalent. I tried the below on Debian 12, using the da_DK.UTF-8 (Danish) locale to be sure this character was not excluded. These were not treated as being the same by





expr





. All three had a different appearance in the terminal program I was using (Konsole).





$ printf '\342\204\253\n'

$ printf '\303\205\n'
Å
$ printf 'A\314\212\n'

$ expr "$(printf '\342\204\253')" = "$(printf '\303\205')"
0
$ expr "$(printf '\342\204\253')" \< "$(printf '\303\205')"
1
$ expr "$(printf '\303\205')" = "$(printf 'A\314\212')"
0
$ expr "$(printf '\303\205')" \> "$(printf 'A\314\212')"
1
$ expr "$(printf '\342\204\253')" = "$(printf 'A\314\212')"
0
$ expr "$(printf '\342\204\253')" \> "$(printf 'A\314\212')"
1



Another difference is that
test

has two separate sets of comparison operators—one set does numerical comparisons and the other does string comparisons. Normally in a script, you would want to be as explicit as possible about the type of comparison you want. However, there might be circumstances where you want the type of comparison to depend on what the two values are. That is how
expr

works, so in that situation you might want to use it instead. It
would

be possible to use a
case

construct in the shell to choose, based on the values, whether to use the arithmetic or string comparison operators with
test

, but
expr

does that for you automatically (see the Appendix for an example of duplicating its behavior).




For matching using the
:

operator,
expr

offers a syntax that is slightly less flexible than
grep

. On some systems,
expr

can be a smaller binary, but at most I've seen it be about 80 kilobytes smaller, so that's unlikely to be a real savings in practice. To get the length of a regular expression match, one could use the
match()

function of
awk

instead. The ability of
expr

to return a subexpression match is neat, but
sed

can also accomplish that.




The


expr





utility first appeared



7

in a version of UNIX from Bell Laboratories called the
Programmer's Workbench



8

1.0, released in 1977. From there,
it made its way into 1979's Seventh Edition UNIX



9

, which is where
the





test





utility made its first appearance



10

. The overlap between the capabilities of the two utilities seems a little strange to me, but I haven't dug into details of the history—perhaps they were developed separately and just happened to meet in Seventh Edition.




It took substantially longer for arithmetic to be a feature built directly into the shell, at least on versions of UNIX from Bell Laboratories/AT&T. The
Korn shell (ksh), included with Eighth Edition UNIX



11

in 1985, offered it with the
let

keyword or
(( ... ))

syntax, but these have not been adopted by POSIX. I couldn't track down exactly when the standard shell gained the syntax
$(( ... ))

for arithmetic expansion used today—it
was not present in 1989's Tenth Edition UNIX



12

, but
did


appear in The Open Group's CAE Specification from 1994



13

(a standard that existed alongside POSIX), so presumably it showed up somewhere in between. Separately, BSD's
C shell (csh) included arithmetic expressions from its first appearance in 2BSD



14

in 1979, very much in line with that shell adopting syntax from the C programming language, so the concept was not new.




My overall conclusion is that almost everything that
expr

does can be performed with another, better-known utility. The exception would be the way the
=

and
!=

operators behave, which to my knowledge isn't duplicated by another standard program. In some cases, using a different utility would require a bit more complex work, so if your script didn't already call that utility, you might be better off with
expr

instead. I am unlikely to start making much use of it, but still enjoy the fact that I learned a bit more about it.






This episode focuses on integer arithmetic because that is what





expr





is capable of doing. More details of arithmetic expansion within the shell are covered in
Hacker Public Radio episode 1951

. If you are instead looking for tools that can do real/floating-point arithmetic, consider







bc







or







awk







. Whiskeyjack briefly discusses these in his recent episode (
Hacker Public Radio 4678

) about high resolution timing. I don't consider either of them to be UNIX Curios, so I don't expect to talk about them in the future.





References:








  1. First Edition UNIX dc manual page

    https://man.cat-v.org/unix-1st/1/dc





  2. Bc specification: Rationale

    https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html#tag_20_09_18





  3. Expr specification

    https://pubs.opengroup.org/onlinepubs/9699919799/utilities/expr.html





  4. GNU multiple precision arithmetic library

    https://en.wikipedia.org/wiki/GNU_Multiple_Precision_Arithmetic_Library





  5. Test specification (2024)

    https://pubs.opengroup.org/onlinepubs/9799919799/utilities/test.html





  6. Test specification (2017)

    https://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html





  7. PWB1 expr manual page

    https://www.tuhs.org/cgi-bin/utree.pl?file=PWB1/usr/man/man1/expr.1





  8. PWB/UNIX

    https://en.wikipedia.org/wiki/PWB/UNIX





  9. Seventh Edition UNIX expr manual page

    https://man.cat-v.org/unix_7th/1/expr





  10. Seventh Edition UNIX test manual page

    https://man.cat-v.org/unix_7th/1/test





  11. Eighth Edition UNIX ksh manual page

    https://man.cat-v.org/unix_8th/1/ksh





  12. Tenth Edition UNIX sh manual page

    https://man.cat-v.org/unix_10th/1/sh





  13. X/Open CAE Specification: Commands and Utilities Issue 4, Version 2

    https://pubs.opengroup.org/onlinepubs/009656399/toc.pdf





  14. An introduction to the C shell (2BSD)

    https://www.tuhs.org/cgi-bin/utree.pl?file=2BSD/doc/csh







Appendix





This shell script shows how one could perform either an arithmetic or string "less than or equal to" comparison using
test

based on the values of the two operands
$a

and
$b

. It makes a string comparison if either operand is null or contains characters other than decimal digits. Otherwise, it does a numerical comparison. It behaves the same as
expr -- "$a" \<= "$b"

, except that
expr

also writes "1" or "0" to standard output depending on whether the comparison is true or false
and

the fact that
=

in
test

means "is identical" rather than "collates the same". I assume you don't care about those, and also that the version of
test

you have supports the
<

and
>

operators.




case "x$a" in
x*[!0-9]*|x)
test "$a" \< "$b" || test "x$a" = "x$b"
;;
*)
case "x$b" in
x*[!0-9]*|x)
test "$a" \< "$b" || test "x$a" = "x$b"
;;
*)
test "$a" -le "$b"
esac
esac



If you really
do

want a "1" or "0" sent to standard output, you could use the following, though things are getting rather complicated at this point. Despite the complexity, it still doesn't handle situations where an error occurs.




case "x$a" in
x*[!0-9]*|x)
test "$a" \< "$b" && echo 1 || { test "x$a" = "x$b" && echo 1 ; } || \
{ echo 0 ; false ; }
;;
*)
case "x$b" in
x*[!0-9]*|x)
test "$a" \< "$b" && echo 1 || { test "x$a" = "x$b" && echo 1 ; } || \
{ echo 0 ; false ; }
;;
*)
test "$a" -le "$b" && echo 1 || { echo 0 ; false ; }
esac
esac







Provide feedback on this episode.

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-19438 | Improper Limitation of a Pathname to a Restricted Directory ('Path Trave…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick