skip to content

Where does the POSIX shell command language actually come from, and how do ksh88/ksh93 and the C shell family (csh and tcsh) relate to it?

level: middleimportance: nice to knowfreq 18%

answer

  1. two lineages, not one family
  2. Bourne then Korn then the standard
  3. the standard was written around ksh88
  4. Berkeley's shell won only interactively
  5. ash is the austere branch

basics

~20 s

POSIX standardised the Bourne shell as extended by David Korn's ksh88, which is why bash, ksh, dash and ash all share one grammar. The C shell is a separate lineage with incompatible syntax; it contributed interactive features such as job control and history, not the scripting language.

solid answer

~50 s

The line runs Bourne shell (V7 Unix, 1979) to ksh, written by David Korn at Bell Labs, whose 1988 version became the basis for the POSIX shell command language. That is the reason bash, ksh, dash, BusyBox ash and zsh all understand the same core grammar — they are converging on one specification, not copying each other. ksh93 then went further with floating-point arithmetic, associative arrays and compound variables, most of which stayed outside POSIX. The C shell is a different family entirely: Bill Joy's csh, later tcsh, brought job control, history substitution and aliases into the interactive world, but its scripting syntax is Bourne-incompatible (`set x = 1`, `if (...) then`) and its quoting and error handling are weak enough that it fell out of use for scripts. Practically, this history is why "POSIX sh" is not the historic Bourne shell — Solaris 10 shipped that at `/bin/sh` and kept its POSIX shell at `/usr/xpg4/bin/sh`.

go deeper

for a junior

Know there are two shell families: the Bourne line (sh, ksh, bash, dash, ash) that share one grammar, and the C shell line (csh, tcsh) that does not. Do not assume a script works in both.

for a middle

Explain that POSIX standardised the Bourne shell as extended by ksh88, which is why every Bourne-family shell shares a core, and name a ksh feature that made it in versus one that did not.

for a senior

Use the history operationally: infer whether a construct is likely portable from where it came from, and recognise legacy environments — old Solaris /bin/sh, tcsh login shells — where the assumptions of a modern script simply do not hold.

for a principal

Frame it as the standardisation lesson it is: an intersection specification is what makes a portability contract possible at all, and it also bounds what that contract can promise when every implementation keeps extending past it.

## Two families, not one Every shell you meet belongs to one of two lineages, and mixing them up is the source of a lot of confusion about what "standard shell syntax" means. **The Bourne family.** Stephen Bourne's `sh` shipped with Version 7 Unix in 1979 and defined the shape everyone still uses: `if ...; then ...; fi`, `case ... esac`, `$var`, backtick command substitution, redirection and pipelines as first-class syntax. David Korn's KornShell, developed at Bell Labs through the 1980s, kept that grammar and added what people were missing — command-line editing, functions with better semantics, arrays, arithmetic, `$( )` substitution. **The C shell family.** Bill Joy's `csh` came out of Berkeley around 1978 with a syntax deliberately made to look more like C, and with interactive features the Bourne shell lacked: job control, history substitution (`!!`, `!$`), aliases and directory stacks. `tcsh` added filename completion and a proper line editor. ## Why POSIX looks like ksh When the shell was standardised — POSIX.2 in 1992, the shell command language that current POSIX still carries — the committee took the Bourne shell as extended by **ksh88** as its base. That single decision explains most of what you see today: - `$(command)` instead of backticks, arithmetic expansion `$(( ))`, functions, `getopts`, parameter expansions like `${var%suffix}` — all ksh contributions that became standard. - bash, dash, BusyBox ash, mksh and zsh all implement that same specified core, which is why a genuinely POSIX script runs unchanged on all of them. - Things ksh had that POSIX did **not** take — arrays being the big one — are exactly the features that break portable scripts today, because bash and ksh both have them and dash does not. ksh93, released in 1993, went considerably further: floating-point arithmetic, associative arrays, compound variables and name references, discipline functions. It was open-sourced in 2000, and clones exist independently — `pdksh` and its descendant `mksh`, and the ksh derivative that OpenBSD ships as its shell. Most of ksh93's later additions never entered the standard, which is why "ksh-compatible" and "POSIX-compatible" are different claims. The other branch of the Bourne family that matters operationally is **ash**, Kenneth Almquist's small clone from 1989. Debian's `dash` and BusyBox's `ash` applet both descend from it, and that ancestry is precisely why they are so austere: they were written to be small, not complete. ## What happened to csh csh's interactive ideas won — job control and history are in every shell now, absorbed via ksh and bash. Its scripting language lost, and comprehensively. It has no functions, its quoting rules mangle whitespace and metacharacters in ways that are hard to work around, its error handling is thin, and redirecting standard error separately from standard output is awkward. The critique became famous enough to have a canonical title ("Csh Programming Considered Harmful"), and the practical consensus for decades has been: use whatever you like interactively, but do not write scripts in it. tcsh still turns up as a default login shell on some BSD and legacy Unix installs, and in scientific and HPC environments where old environment-module setups were written for it. If you inherit one, the tell is the syntax: ```csh set path = ($path /opt/tool/bin) if ( -e /etc/motd ) then echo present endif ``` None of that is Bourne syntax, and nothing you know about POSIX quoting applies to it. ## Why any of this matters in practice Three payoffs. First, it explains why "POSIX sh" is not the *original* Bourne shell: Solaris 10 shipped the genuine pre-POSIX Bourne shell at `/bin/sh`, which lacked `$( )` and functions-with-`local` behaviour people assumed were universal, and put a conforming shell at `/usr/xpg4/bin/sh`; Solaris 11 finally pointed `/bin/sh` at ksh93. Scripts written against "POSIX" broke on the older one for exactly this reason. Second, it tells you which extensions to expect where. A construct that came from ksh88 is probably standard and safe; one that arrived in ksh93 or bash is probably not. Third, it is the reason a portable script has one grammar to target at all. Without the ksh88-based standardisation there would be no meaningful `#!/bin/sh` contract — just a pile of mutually incompatible shells, which is what the 1980s actually looked like.

  • Name a construct that came from ksh and is now standard, and one that stayed an extension.
    `$(command)` substitution, arithmetic expansion `$(( ))`, `getopts` and the `${var%suffix}` expansion family all came from ksh and are specified by POSIX. Arrays are the headline extension that did not make it in — bash and ksh have them, dash does not, and that gap is behind a large share of real portability failures.
  • Why do people still say never to write scripts in csh or tcsh?
    Its scripting language is genuinely weak: no functions, quoting rules that mangle whitespace and metacharacters, awkward separation of stdout and stderr, and thin error handling. Its good ideas — job control, history, aliases — were absorbed into the Bourne family decades ago, so there is nothing left to gain and a lot of sharp edges to inherit.

saying these in an interview costs you the question

  • Says POSIX sh is simply the original Bourne shell
  • Believes csh and sh differ only in interactive features
  • Assumes ksh arrays are portable because ksh predates POSIX
  • Thinks bash's grammar was copied from ksh rather than standardised
  • Treats tcsh as a drop-in replacement for a Bourne-family shell

context