Skip to content
PodcastsEducationHacker Public Radio

Hacker Public Radio

Hacker Public Radio
Hacker Public Radio
Latest episode

330 episodes

  • Hacker Public Radio

    HPR4710: Civilization VI

    21/08/2026
    This show has been flagged as Clean by the host.

    Civilization VI, released in 2016, was a further evolution of the franchise that added interesting new features. We introduce it in this episode.

    Civilization VI

    Released in 2016, Civ VI is probably still the most played version of the game. Although there has been a general gap of 5-6 years between versions, the effects of the Covid-19 pandemic slowed down many things. Since early 2025 we have had Civilization VII come out, but in my opinion it is still being tweaked and refined. In the meantime, Civilization VI is the game I play now when I play Civ. I’ve played all of them from the first, and put in many hours here. Since I now play Civ on Steam mostly, I have statistics! I played Civ V for a total of 769 hours, and so far I have played Civ VI for 689 hours, but since this is what I play for now I will probably exceed my Civ V hours pretty soon. BTW, I probably have well over 1000 hours on Civ II, which I think I have played the most, and certainly more than 3000 hours total over all all versions.

    I’m going to cover this without worrying too much about whether it is the base game or an expansion. Fact is, Firaxis have started moving to something more akin to a subscription model when they introduced the New Frontier Pass, which works very well when you are on Steam. The idea was that you bought this pass, and very few months got new content, and on Steam it would automatically be downloaded to your account. That is not to say that there weren’t actual expansions, but since the last thing they released was this “pass”, i’m kind of wondering what they will do with Civ VII. In any case, let’s take a look at this.

    Gameplay

    Again, you start with a tribe in 4000BC which founds your first city, and you immediately begin building units, exploring, researching, and developing your city. this is a time-tested mechanic that never changes. but there are some interesting changes that make this game fascinating.

    First, in the earlier versions of Civ you thought of yourself as playing a particular civ, which came with a particular leader. In different versions of Civ the name of the leader might change, like for France you might have Napoleon on some versions, or Joan of Arc in another, but in each case one civ gets one leader, so you tended to think of yourself as playing the nation. Rome might be Caesar in one version, Augustus in another, or Trajan in yet another. (Fun fact: Sid Meier says he always plays as Rome because it just feels right to him). In Civ VI this linkage is broken. One leader, Eleanor of Aquitaine, can be played as the Queen of England, or she can be played as the Queen of France. And each version has different abilities and strengths. If you want a fairly easy Culture victory, play her as Queen of France, and watch as other players’ cities rebel and join your Empire because they admire you so much. Teddy Roosevelt also has two different manifestations, Rough Rider Teddy, and Big Stick Teddy, when you have all the expansions. China has two possible leaders, with different strengths: Qin Shi Huang, and Kublai Khan. And Kublai Khan can be either Chinese or Mongolian, with different strengths. So in Civ VI you don’t think of what Civ you want to play, you think of which leader you want to play.

    Every leader comes with a Civilization Ability, a Unique Unit, and a Unique Infrastructure. These matter a lot. For example, take John Curtin, the leader of the Australian civilization. His Civilization Ability is called Land Down Under, and it gives him +3 Housing in coastalcities. Building a Pasture triggers a Culture Bomb, claiming surrounding tiles. Campus, Commercial Hub, Holy Site, and Theater Squaredistricts gain +1 to their yields in tiles with Charming Appeal, and +3 in tiles with Breathtaking Appeal. Then his Unique Unit is the Digger, a melee military unit that replaces the Infantry unit. And his Unique Infrastructure is tile improvement called the Outback Station. This gives you increased food, production, and housing, some of which varies according to what is on surrounding tiles. The point is that each leader gives you very specific advantages that you should play to. In Civ VI it is even more important to tailor your strategy to the particular leader. Leaders like Frederic Barbarossa or Simon Bolivar are very good for Domination, while Peter the Great and Saladin are very good for Religious victories. If you take a random leader be ready to adapt your play style and strategy to fit, or alternatively decide what kind of victory you want to achieve and pick a leader who is good at it. Or if you want a real challenge, pick a leader who is not suited to the victory condition you are aiming at. But keep in mind that the game may take you in places you didn’t expect. One game I picked Wilfrid Laurier of Canada, who is ideal for Diplomatic and Cultural victories, and I thought I would be going for a Cultural victory. But Wilfrid’s strengths come from his advantages on Tundra land tiles, and I somehow managed to spawn in the middle of the map with no Tundra tiles at all in my empire. I had work hard to manage a Science victory in that case.

    Districts

    This is a major change in Civ. In earlier versions you built buildings inside of cities, and you tended to build the same buildings in most or all cities. You were only limited by your ability to generate Production to get them done. Civ VI is different. You have to place Districts on specific tiles, and and you can then develop the district with added buildings. If you want to develop your industry, you can place an Industrial Zone, and then add a Workshop, then a Factory, then a Power plant, and so on, each one increasing your Production. For Science you place down a Campus District, then a Library, then a University, then a Research Lab. And the game mechanic means you cannot built everything in every city. You can place one district at the start, then one more when your city reaches a population of 4, then a third one when you reach 7, and so on with each added population of three allowing one more district. The point is to force you to make choices. Districts get adjacency bonuses which increase their output. The Campus (generates Science) and the Holy Site (generates Faith) both benefit from adjacent Mountains, and the effect is additive. So if you find a tile with three mountains around it, you will want to place one of these districts there. Districts also get adjacency bonuses from other districts, so the planning the layout of your cities is really important to maximize these bonuses. And they can get bonuses even with adjacent Districts that belong to a different city!

    Another key district is the Encampment, which let you build better, more veteran military units. Since building units requires Production, you should build build an Encampment in a city which has a lot of Production, and that probably means one with an Industrial Zone. A Harbor District would go in a city with coastal tiles, of course. All of this requires planning. Expert players will usually start out by exploring to see what the map looks like around them, what terrain features they have to work with, what resources are available, and so on. Then, around turn ten they will plan out their next cities, and figure out where they want to place districts and so on. There is a built-in “map pin” system for this, and it can be improved by installing the right mod. While you don’t have to do this, it does help you to be successful.

    Maps and Terrain

    Terrain features matter a lot in this game. We already mentioned adjacency bonuses of Mountains for Holy Sites and Campuses, but there is much more. The map is divided into Continents, and it is quite normal for a contiguous land mass to contain two or more continents if it is large enough. Think of how Europe and Asia are really just one large land mass to see what I mean. This matters because certain units may defend differently depending on which continent they are on, or some civilizations may be affected from having cities on a different continent. Terrain features like Rivers are extremely important for the placement of cities because they provide fresh water. You can sometimes get around that by building an Aqueduct to bring water from a Lake or a Mountain, but that means using up one of your city tiles, and spending production to build it. If you can place your city on a river you can avoid that problem and get off to a fast start. But the downside is that rivers can flood, and that can wreck your improvements. A big idea for terrain is Appeal. Each tile has an Appeal level ranging from Disgusting to Breathtaking, and it is determined by a combination of the tile’s features themselves and by adjacencies. The modifiers are added together to determine the Appeal level of the tile, and there is a lens you use to view your Appeal levels on all tiles, which are color coded. This matters particularly if you are trying for a Culture victory. Culture victories are own by generating Tourism, and while some that can come from things like Museums and Wonders, you will really want to maximize your Tourism to win this game, and two good ways to do that are building National Parks and Seaside Resorts. You can only do that if the appeal is at least Charming (or even better, Breathtaking). If you find you cannot build them in what seems like a good spot, chances are Appeal is the problem. So check that out. You can, for instance, remove marshes (which give negative appeal), and plant forests (which add appeal), and that might be all it takes to get these going.

    The underlying issue in managing your cities is that you only have so many tiles, and a tile that might be great for a farm might also be great for a District. You need food to grow, and you need to grow to put down Districts, so you are always making choices about this, and that is what makes this a great strategy game.

    Other Resources

    Civilization 6 Tutorial: In Game Settings

    Links

    https://www.pcgamesn.com/civilization-vii/narrative-lead-job-posting

    https://civilization.fandom.com/wiki/Qin_Shi_Huang_(Civ6)

    https://civilization.fandom.com/wiki/Kublai_Khan_(Chinese)_(Civ6)

    https://civilization.fandom.com/wiki/Housing_(Civ6)

    https://civilization.fandom.com/wiki/Coastal_(Civ6)

    https://civilization.fandom.com/wiki/Pasture_(Civ6)

    https://civilization.fandom.com/wiki/Tile_(Civ6)

    https://civilization.fandom.com/wiki/Campus_(Civ6)

    https://civilization.fandom.com/wiki/Commercial_Hub_(Civ6)

    https://civilization.fandom.com/wiki/Holy_Site_(Civ6)

    https://civilization.fandom.com/wiki/Theater_Square_(Civ6)

    https://civilization.fandom.com/wiki/District_(Civ6)

    https://civilization.fandom.com/wiki/Appeal_(Civ6)

    https://civilization.fandom.com/wiki/Continent_(Civ6)

    https://www.youtube.com/watch?v=lkas_uiq_fk

    https://www.palain.com/gaming/civilization-vi/

    Provide feedback on this episode.
  • Hacker Public Radio

    HPR4709: Peertube: my two penneth

    20/08/2026
    This show has been flagged as Clean by the host.

    Kevie gives a brief talk about
    PeerTube
    and also some of the issues that he has experienced, especially with the Android apps, and shares his subscriptions:







    More Fun Making It






    Main Oldschooleducation channel






    The Linux Experiment






    Nick's Workshop






    Arthurpizza






    Josh's Junk Drawer






    Reece's Rambles






    Gardner Brynat






    Veronica Explains






    Andy Piper








    Provide feedback on this episode.
  • Hacker Public Radio

    HPR4708: Programmable Logic Controls - Episode 1

    19/08/2026
    This show has been flagged as Clean by the host.

    Introduction







    01



    This is the first episode in an 8 part series.







    02



    This series is on programmable logic controllers, or PLCs as they are commonly known.







    What is a PLC you ask?



    In short, it is a general purpose programmable industrial control device.



    They are used all through industry in factories, utilities, and anywhere industrial machines need to be controlled.







    03



    They are a fully integrated system of hardware, software, and development environment tailored specifically for automatic industrial equipment.







    It is probably easiest to describe them by first giving a bit of historical background as to what preceded them.







    04 The Early Days of Automation







    You have probably heard of the Jacquard loom of the early 19th century and its use of punch cards with respect to it being one of the technologies which eventually lead to computing.



    However, for our purposes here, its significance is that it was an early form of industrial automation control system, since it was after all controlling a machine in a factory.







    05



    For other types of machines, a common purely mechanical means of having the various parts of a complex machine move in a coordinated manner was through the use of shafts and cams.







    A series of cams located on a shaft or set of shafts connected by gears, could move parts of a machine, turn valves on or off, and generally coordinate the parts of a machine.







    With the spread of the use of electricity in factories in the early 20th century, it was now possible to use the new electrical technology to perform control and automation functions.











    06 The Background of Relay Logic







    I will need to explain some electromechanical terminology here as it is necessary to understand these terms and the concepts behind them in order to understand PLCs, as the latter is an evolution of its predecessors and uses the same terms.







    What preceded PLCs was what was commonly known as "relay logic".







    07 What is a Relay



    A relay is an electromechanical device that responds to and controls the flow of electric current.



    A relay has a "coil" which as the name implies is a coil of conductive wire.



    When you energize the coil, that is you apply electric current to it, it forms an electromagnet.







    08



    This electromagnet draws in an armature.



    The armature is a moving piece of metal which is attracted to the coil when the latter is energized.



    The relay being activated is referred to as closing.



    Deactivating is referred to as opening.



    Alternative names for these states are "pulls in" and "drops out" respectively.







    09



    The armature in turn is attached to one or more contacts.



    You can think of a contact as being like a switch.



    When the switch is turned one way, the electrical path is closed and current can flow.



    When it is turned the other way, the electrical path is broken and current flow is interrupted.







    10



    Contacts can be normally open, in which case the path is closed and current can flow when the relay turns on.



    Alternatively, they can be normally closed, in which case the path is closed and current can flow when the relay turns off.







    A relay can, and typically does, have multiple contacts, including both normally open and normally closed.







    Remember these terms, coil, contact, normally open, and normally closed.







    11 What is a Latching Relay



    There is a special type of relay known as a latching relay.



    This was only occasionally used in relay control circuits, but the concepts behind it will become more important when we talk about actual PLCs.



    A latching relay has two coils.



    One coil is used to turn on or "set" the relay.



    The other coil is used to turn off or "reset" the relay.







    12



    The relay has either a mechanical latching mechanism or a magnet which is used to hold the relay in the set (or in other words, closed) position even if the set coil is de-energized.



    You must energize the reset coil in order to reset or "open" the relay.



    A latching relay provides the equivalent of one "bit" of memory which retains its last state even if the power to the machine is turned off.







    13



    Latching relays were used in applications where it was important that the relay logic circuit remember its last state.



    While not frequently used in relay control circuits due to their greater cost and complexity, the concept was to be more extensively used in PLC programs when set and reset instructions were provided which performed the equivalent function in software where there were no similar cost considerations to worry about.







    14



    Thus set and reset were to be much more frequently used in PLC programs than they were when they were actual relays.







    Add these terms to your list of things to remember - latch, unlatch, set and reset.











    15 What is a Contactor



    I will mention another term now in case it happens to come up later.



    This is "contactor".



    A contactor is basically just a large relay.



    It is typically used to control large electrical loads such as motors and heaters.







    16



    Smaller relays are typically just called "relays", or sometimes "control relays".



    These be used to create either logic circuits or control smaller electrical loads such as lights or pneumatic or hydraulic valves.







    This may seem like a lot of jargon, but bear with me, I will make analogies to computers when appropriate.











    17 Input and Output Devices



    If you want to do some useful work, you will need some I/O.







    Typical input devices include the following.







    18



    Push buttons.



    These are buttons which are pressed by the operator to command the machine to do something.







    19



    Selector switches.



    These are switches which are typically rotated to turn on or off and maintain their position.



    These can have multiple positions, each of which can activate a separate input.







    20



    Pilot lights. These are lights which are used to provide feedback to the operator.







    21



    Limit switches.



    These are mechanical switches which parts of the machine activate, rather than the operator activating them.



    This can be used to determine what position the various parts of the machine are in at any given time.







    22



    Proximity sensors.



    These are essentially solid state limit switches which are more reliable than mechanical switches as they are less subject to wear and tear.



    These are often colloquially abbreviated as "proxies".







    23



    Solenoid valves.



    These are like relays in that an electromagnetic coil is activated.



    However, instead of activating another electrical contact, it activates a pneumatic or hydraulic valve.



    This valve in turn typically allows air or hydraulic fluid to move a piston within a cylinder, which then moves some mechanical part of the machine.







    A typical machine will have lots and lots of proximity sensors and solenoid valves.











    24 Relay Logic







    All of these inputs and outputs require some sort of logic to coordinate them.



    This is where control relays came in.







    25



    If having the output of one electrical device being able to control another electrical device sounds a bit like a transistor, then yes relays are analogous to electronic transistors.



    However, while transistors are a mid 20th century invention, relays date from the mid 19th century.







    26



    Like with transistors, it is possible to encode logic into a network of wires connecting relays together, along with inputs from switches and other input devices.







    By wiring sensors and relays together in the right order, it is possible to create reasonably complex sequences of operation to control a machine in an automatic manner.







    27 Relay Boolean Logic



    Wiring relay contacts in series creates "and" conditions.



    Wiring them in parallel creates "or" conditions.



    Using normally closed relay contacts creates "not" conditions.



    By feeding a relay's own contacts back into the circuit leading up to its coil, it is possible to have a relay remember its own state.



    Each relay therefore could be thought of as one bit of logic in a boolean logic circuit.







    28 Timers and Counters



    Special timing relays could be used to create a time delay between the relay energizing or de-energizing, and the contacts closing or opening.



    A timing relay which imposed a delay after being energized is an "on delay timer".



    A timing relay which imposed a delay after being de-energized is an "off delay timer".







    29



    Timing relays were often pneumatic.



    They had a small rubber bellows that leaked air slowly through a adjustable orifice.



    The bellows would prevent the relay from opening or closing, depending on the type, until enough air had leaked out for it to close or open.







    For more accuracy or for longer time delays, motor driven clock timers could be used.







    30



    Special counter relays could be used to require an input to turn on or off a specified number of times before activating the relay outputs.







    Counters typically used a ratchet mechanism to count up to a preset before activating.







    31



    Later timing and counting relays used electronic timers and counters, but these arrived relatively late at a time when relay logic was on its way out.











    32 Electrical Panels



    Large numbers of relays would be mounted on panels in large electrical enclosures, with the wires running between them, and also out to the limit switches, valves, and other devices mounted on the machine.











    33 Designing the Logic Circuit







    The logic or program would be encoded in the selection of the devices and in the wires running between them.







    To design and document this logic, the designer would create electrical drawings.







    These drawings would follow one of two different styles.







    34



    In one style, the two power wires, either hot and neutral in the case of AC power, or positive and negative in the case of DC power, were drawn as vertical lines down each side of the page of the drawing.



    These vertical lines are known as the "rails".







    Wires would then be drawn horizontally across the page from left to right showing the connections between push buttons, switches, and relay contacts across to the relay coils or pilot lights on the right.







    35



    The physical connections between relay coils and relay contacts were not normally shown on the drawings, although they may in the case of push buttons or selector switches with multiple contacts.



    Instead you would rely on the labels or names to see which contacts were associated with which relay coils, limits switches, or push buttons.







    36



    There are standard naming conventions for devices which I won't go into here.



    However, this means that the relay coils could be on one page of the drawing set, and the contacts could be anywhere else in the drawing set.



    It was convention to write a cross reference beside the relay coil to list where its contacts are used in the drawing.







    37 Ladder Rungs



    In practical terms, what this meant is that the logic drawing tended to take the form of many independent horizontal sets of wiring and contacts looking rather like the rungs of a ladder.



    Thus these drawings came to be known as "ladder diagrams" or "ladder drawings".



    The horizontal elements were known as "rungs".







    38 Standard Symbols



    Each type of device had a standard symbol so you could see at a glance what it was.



    For example a relay coil was a circle.



    A normally open relay contact was two short vertical lines separating a break in the wire circuit.



    A normally closed relay contact was just a normally open contact with a diagonal line drawn across it.







    39 Logic Flow



    By convention, drawings were read from top to bottom, left to right.







    In actual practice operations could happen in parallel and you had to be careful to avoid introducing what was called a "relay race" where the outcome of an operation depended upon which of two parallel operations completed first.



    A relay race could produce unpredictable results depending upon which of the operations completed first and therefore must be avoided.







    40



    Engineers, technicians, and electricians were expected to become proficient in reading and understanding these ladder diagrams in order to design and troubleshoot equipment.



    To someone who was experienced in the field, reading and understanding these diagrams became second nature.







    41 DIN Drawings



    I mentioned there were two styles of drawings.



    The other style is known as DIN, which stands for Deutsches Institut fur Normung, or German Standards Institute in English.



    This is basically just a ladder diagram turned on its side, with the rails running horizontally from left to right, and the rungs running vertically from top to bottom.







    I have mentioned DIN drawings here for completeness, but even those countries which still use DIN style drawings for documenting wiring use vertical ladders when ladder logic went electronic.











    42 Comparison to Electronic Logic Gates



    If you are familiar with electronic and, or, nand, and nor gates some of the above concepts should familiar to you.



    Some of the concepts are indeed analogous.



    However these electromechanical conventions predate the existence of solid state.







    43



    The drawings also follow completely different principles.



    Whereas solid state logic gate drawings show inputs and outputs on a single device grouped together in a single block with complex interconnections, electrical ladder drawings separate them, which has a number of very significant consequences.







    44



    First it greatly simplifies drawing the interconnections.



    Second, it allows inputs and outputs to be grouped together by function rather than by physical packaging.



    Thirdly it allows drawings to be spread over many smaller standard size sheets which in turn allows a functional collection of related rungs to be viewed in a single glance.







    These three factors were to be of great significance when physical relays were replaced by software.







    --------------------







    45 Historical Origins and Development







    Origins



    Nobody seems to know when or where industrial relay logic was first introduced.



    Best guesses seem to say some time after the widespread electrification of factories in the early 20th century.



    Automation in the era of steam power seemed to rely mainly on the profile of mechanical cams mounted on shafts to time machine operations.



    Most sources seem pretty confident that relay logic was widespread by the 1940s or 50s.







    46 Solid State



    The introduction of transistors did not immediately displace relays for control purposes.



    Relays had the ability to handle significant amounts of power directly, allowing them to interface with inputs and outputs directly.



    Transistors would have still required interposing relays anyway, increasing the component count and complicating the system for little benefit.







    47



    There were solid state logic modules developed specifically to replace relays in control logic, but these were not widely used.



    These solid state logic modules still required wiring connections between them, limiting their advantages over electromechanical relays.



    A number of families of electromechanical control relays were developed by a number of companies, and these tended to be very robust and reliable, although rather expensive.











    48 Conclusion







    In this episode I have covered the following.







    What is a relay.



    Common input and output devices.



    How relays are used to create boolean logic systems.



    Designing relay logic systems and the electrical drawings used to document them.



    The brief history of relay logic systems.







    49 Terms to Remember







    Here are some terms you may wish to remember.







    Programmable Logic Controller, also known as "PLC"



    Control relay



    Coils



    Contacts



    Normally open



    Normally closed







    50



    Latch



    Unlatch



    Set



    Reset







    51



    On delay timers



    Off delay timers



    Counters



    Push buttons



    Pilot lights



    Limit switches



    Proximity switches, also known as "proxies"



    Ladder diagrams



    Ladder rails



    Ladder rungs







    52 Next Episode







    In the next episode I will discuss the following



    The origins of PLCs.



    How they evolved from relay logic.



    How and why physical relays and wiring became virtual relays in software.







    53



    Did you think that visual or graphical programming was something new?



    Listen to the next episode and find out that people were doing it in factories half a century ago back when you were still trying to get your first Fortran program to run on a mainframe.







    54



    This has been the first episode in an 8 part series.







    --------------------





    Provide feedback on this episode.
  • Hacker Public Radio

    HPR4707: UNIX Curio #12 - expr

    18/08/2026
    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:







    First Edition UNIX dc manual page
    https://man.cat-v.org/unix-1st/1/dc





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





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





    GNU multiple precision arithmetic library
    https://en.wikipedia.org/wiki/GNU_Multiple_Precision_Arithmetic_Library





    Test specification (2024)
    https://pubs.opengroup.org/onlinepubs/9799919799/utilities/test.html





    Test specification (2017)
    https://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html





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





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





    Seventh Edition UNIX expr manual page
    https://man.cat-v.org/unix_7th/1/expr





    Seventh Edition UNIX test manual page
    https://man.cat-v.org/unix_7th/1/test





    Eighth Edition UNIX ksh manual page
    https://man.cat-v.org/unix_8th/1/ksh





    Tenth Edition UNIX sh manual page
    https://man.cat-v.org/unix_10th/1/sh





    X/Open CAE Specification: Commands and Utilities Issue 4, Version 2
    https://pubs.opengroup.org/onlinepubs/009656399/toc.pdf





    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.
  • Hacker Public Radio

    HPR4706: Quirks and Customisations

    17/08/2026
    This show has been flagged as Clean by the host.

    System Quirks & Tweaks









    System Info:-










    CPU:

    16-core AMD Ryzen 9 3950X (-MT MCP-)

    speed/min/max:

    2433/2200/4761 MHz





    Kernel:

    6.8.0-134-generic x86_64

    Up:

    18h 28m

    Mem:

    17.58/62.73 GiB (28.0%)





    Storage:

    3.64 TiB (81.9% used)

    Procs:

    616

    Shell:

    Bash

    inxi:

    3.3.34









    Some of the hacks:-








    Shell & Terminal Setup







    Visual Host Identifiers:
    I use custom terminal prompt colors and styles on different boxes so I can immediately see which machine I am logged into.





    Command Line Overrides:
    Standard tools are aliased to modern alternatives (
    cat
    to
    batcat
    ,
    df
    to
    duf
    ,
    du
    to
    dust
    ,
    ls
    to
    eza
    ).





    Word-Break Tweak:
    My shell config strips
    -
    and
    .
    from word-break rules so tab-completion handles hyphenated or dotted filenames as single words.





    History Search:
    I mapped
    Ctrl+F
    (instead of
    Ctrl+R
    ) to run a custom, colorized, interactive fuzzy history search using
    fzf
    .





    Local AI Tooling:
    I use AI coding interfaces, with
    ollama
    aliased to run Qwen Coder by default.





    Complex Toolchains:
    My system runs multiple co-existing environments, including conflicting Java runtimes (8 and 17), Android SDK, Rust, Go, and local Perl/Python libraries.









    Autostart & Background Services







    Audio & RGB Tweaks:
    Custom services handle Bluetooth switching for my Bose QC35 headphones, and cycled my keyboard lighting profiles based on the time of day.





    Passive Time Tracker:
    A custom background system tracks files via
    inotifywait
    and polls network connections to log my work hours into a 15-minute rounded log file.





    Typo Alert Keylogger:
    A root-owned background script buffered my keystrokes globally to run a spellcheck, firing a desktop alert when I made a typo.





    Work/Life Balance:
    Discord is configured to launch only on weekends or after 17:30 on weekdays.









    Custom Applications & Launchers







    Local LLM Voice Assistant:
    A desktop launcher opened "Marvin," a private local voice-chat assistant.





    Custom Remote Control:
    A custom script used ADB and Monkeyrunner to mirror and control an Android tablet screen in a local window.





    KWin Layout Fixes:
    Joplin is set to bypass the taskbar.









    Custom Panel Widgets







    Weather & Brightness:
    Custom scripts feed my desktop panel with weather emojis from the 7Timer! API and let me change external monitor brightness using
    ddcutil
    over I2C.





    Notification Pipelines:
    Custom shell scripts polled for SMS and email alerts, using MD5 hashes of the text blocks to ensure I never got duplicate notifications.





    Fake GPS Server:
    A tiny local server on port 22222 returns my home coordinates to any local app requesting geolocation.









    Repurposed "Radio" Panel







    Hardware Recycling:
    An old Android tablet with a broken screen is used as a green-screen terminal. It runs a custom WebView app to display ambient data.





    Gmail Push Pipeline:
    A local PHP script runs via cron every 10 minutes to fetch new email alerts and send them to the tablet over the local network.







    Provide feedback on this episode.
More Education podcasts
About Hacker Public Radio
Hacker Public Radio is an podcast that releases shows every weekday Monday through Friday. Our shows are produced by the community (you) and can be on any topic that are of interest to hackers and hobbyists.
Podcast website

Listen to Hacker Public Radio, The Comeback with Brenda Dennehy and many other podcasts from around the world with the radio.net app

Get the free radio.net app

  • Stations and podcasts to bookmark
  • Stream via Wi-Fi or Bluetooth
  • Supports Carplay & Android Auto
  • Many other app features