That said, the Korn shell — as a thing distinct from the POSIX shell — never really became popular outside the commercial Unix world. This is because its rise corresponded with the early years of Unix commercialization, so it got caught up in the Unix wars. BSD Unixes eschewed it in favor of the C shell, and its source code wasn't freely available for use in Linux when it got started.⁵ So, when the early Linux distributors went looking for a command shell to go with their Linux kernel, they usually chose GNU Bash, one of those sh
-compatibles you're talking about.⁶
That early association between Linux and Bash pretty much sealed the fate of many other shells, including ksh
, csh
and tcsh
. There are die-hards still using those shells today, but they're very much in the minority.⁷
I used "shell script" above as a generic term meaning Bourne/POSIX shell scripting. This is due to the ubiquity of Bourne family shells. To talk about scripting on other shells, you need to give a qualifier, like "C shell script." Even on systems where a C family shell is the default interactive shell, it is better to use the Bourne shell for scripting.
There are many ways to check whether a given Bourne family shell script is portable:
Early versions of BSD Unix were just add-on software collections for V6 Unix. Since the Bourne shell wasn't added to AT&T Unix until V7, BSD didn't technically start out having the Bourne shell. BSD's answer to the primitive nature of the Thompson shell was the C shell.
Nevertheless, the first standalone versions of BSD (2.9BSD and 3BSD) were based on V7 or its portable successor UNIX/32V, so they did include the Bourne shell.
(The 2BSD line turned into a parallel fork of BSD for Digital's PDP minicomputers, while the 3BSD and 4BSD lines went on to take advantage of newer computer types like Vaxen and Unix workstations. 2.9BSD was essentially the PDP version of 4.1cBSD; they were contemporaneous, and shared code. PDPs didn't just disappear when the VAX arrived, so the 2BSD line is still shambling along.)
It is safe to say that the Bourne shell was everywhere in the Unix world by 1983. That's a good approximation to "forever" in the computing industry. MS-DOS got a hierarchical filesystem that year (awww, how cuuute!) and the first 24-bit Macintosh with its 9" B&W screen — not grayscale, literally black and white — wouldn't come out until early the next year.
The Thompson shell was quite primitive by today's standards. It was only an interactive command shell, rather than the script programming environment we expect today. It did have things like pipes and I/O redirection, which we think of as prototypically part of a "Unix shell," so that we think of the MS-DOS command shell as getting them from Unix.
The Bourne shell also replaced the PWB shell, which added important things to the Thompson shell like programmability (if
, switch
and while
) and an early form of environment variables. The PWB shell is even less well-remembered than the Thompson shell since it wasn't part of every version of Unix.
When someone isn't specific about POSIX vs Bourne shell compatibility, there is a whole range of things they could mean.
At one extreme, they could be using the 1979 Bourne shell as their baseline. An "sh
-compatible script" in this sense would mean it is expected to run perfectly on the true Bourne shell or any of its successors and clones: ash
, bash
, ksh
, zsh
, etc.
Someone at the other extreme assumes the shell specified by POSIX as a baseline instead. We take so many POSIX shell features as "standard" these days that we often forget that they weren't actually present in the Bourne shell: built-in arithmetic, job control, command history, aliases, command line editing, the $()
form of command substitution, etc.
Although the Korn shell has roots going back to the early 1980s, AT&T didn't ship it in Unix until System V Release 4 in 1988. Since so many commercial Unixes are based on SVR4, this put ksh
in pretty much every relevant commercial Unix from the late 1980s onward.
(A few weird Unix flavors based on SVR3 and earlier held onto pieces of the market past the release of SVR4, but they were the first against the wall when the revolution came.)
1988 is also the year the first POSIX standard came out, with its Korn shell based "POSIX shell." Later, in 1993, an improved version of the Korn shell came out. Since POSIX effectively nailed the original in place, ksh
forked into two major versions: ksh88
and ksh93
, named after the years involved in their split.
ksh88
is not entirely POSIX-compatible, though the differences are small, so that some versions of the ksh88
shell were patched to be POSIX-compatible. (This from an interesting interview on Slashdot with Dr. David G. Korn. Yes, the guy who wrote the shell.)
ksh93
is a fully-compatible superset of the POSIX shell. Development on ksh93
has been sporadic since the primary source repository moved from AT&T to GitHub with the newest release being about 3 years old as I write this, ksh93v. (The project's base name remains ksh93
with suffixes added to denote release versions beyond 1993.)
Systems that include a Korn shell as a separate thing from the POSIX shell usually make it available as /bin/ksh
, though sometimes it is hiding elsewhere.
When we talk about ksh
or the Korn shell by name, we are talking about ksh93
features that distinguish it from its backwards-compatible Bourne and POSIX shell subsets. You rarely run across the pure ksh88
today.
AT&T kept the Korn shell source code proprietary until March 2000. By that point, Linux's association with GNU Bash was very strong. Bash and ksh93
each have advantages over the other, but at this point inertia keeps Linux tightly associated with Bash.
As to why the early Linux vendors most commonly choose GNU Bash over pdksh
, which was available at the time Linux was getting started, I'd guess it's because so much of the rest of the userland also came from the GNU project. Bash is also somewhat more advanced than pdksh
, since the Bash developers do not limit themselves to copying Korn shell features.
Work on pdksh
stopped about the time AT&T released the source code to the true Korn shell. There are two main forks that are still maintained, however: the OpenBSD pdksh
and the MirBSD Korn Shell, mksh
.
I find it interesting that mksh
is the only Korn shell implementation currently packaged for Cygwin.
GNU Bash goes beyond POSIX in many ways, but you can ask it to run in a more pure POSIX mode.
csh
/tcsh
was usually the default interactive shell on BSD Unixes through the early 1990s.
Being a BSD variant, early versions of Mac OS X were this way, through Mac OS X 10.2 "Jaguar". OS X switched the default shell from tcsh
to Bash in OS X 10.3 "Panther". This change did not affect systems upgraded from 10.2 or earlier. The existing users on those converted systems kept their tcsh
shell.
FreeBSD claims to still use tcsh
as the default shell, but on the FreeBSD 10 VM I have here, the default shell appears to be one of the POSIX-compatible Almquist shell variants. This is true on NetBSD as well.
OpenBSD uses a fork of pdksh
as the default shell instead.
The higher popularity of Linux and OS X makes some people wish FreeBSD would also switch to Bash, but they won't be doing so any time soon for philosophical reasons. It is easy to switch it, if this bothers you.
It is rare to find a system with a truly vanilla Bourne shell as /bin/sh
these days. You have to go out of your way to find something sufficiently close to it for compatibility testing.
I'm aware of only one way to run a genuine 1979 vintage Bourne shell on a modern computer: use the Ancient Unix V7 disk images with the SIMH PDP-11 simulator from the Computer History Simulation Project. SIMH runs on pretty much every modern computer, not just Unix-like ones. SIMH even runs on Android and on iOS.
With OpenSolaris, Sun open-sourced the SVR4 version of the Bourne shell for the first time. Prior to that, the source code for the post-V7 versions of the Bourne shell was only available to those with a Unix source code license.
That code is now available separately from the rest of the defunct OpenSolaris project from a couple of different sources.
The most direct source is the Heirloom Bourne shell project. This became available shortly after the original 2005 release of OpenSolaris. Some portability and bug fixing work was done over the next few months, but then development on the project halted.
Jörg Schilling has done a better job of maintaining a version of this code as obosh
in his Schily Tools package. See above for more on this.
Keep in mind that these shells derived from the 2005 source code release contain multi-byte character set support, job control, shell functions, and other features not present in the original 1979 Bourne shell.
One way to tell whether you are on an original Bourne shell is to see if it supports an undocumented feature added to ease the transition from the Thompson shell: ^
as an alias for |
. That is to say, a command like ls ^ more
will give an error on a Korn or POSIX type shell, but it will behave like ls | more
on a true Bourne shell.
Occasionally you encounter a fish
, scsh
or rc/es
adherent, but they're even rarer than C shell fans.
The rc
family of shells isn't commonly used on Unix/Linux systems, but the family is historically important, which is how it earned a place in the diagram above. rc
is the standard shell of the Plan 9 from Bell Labs operating system, a kind of successor to 10th edition Unix, created as part of Bell Labs' continued research into operating system design. It is incompatible with both Bourne and C shell at a programming level; there's probably a lesson in there.
The most active variant of rc
appears to be the one maintained by Toby Goodwin, which is based on the Unix rc
clone by Byron Rakitzis.
Best Answer
It's not as simple as supporting
local
or not. There is a lot of variation on the syntax and how it's done between shells that have one form or other of local scope.That's why it's very hard to come up with a standard that agrees with all. See http://austingroupbugs.net/bug_view_page.php?bug_id=767 for the POSIX effort on that front.
local scope was added first in ksh in the early 80s.
The syntax to declare a local variable in a function was with
typeset
:(function support was added to the Bourne shell later, but with a different syntax (
f() command
) andksh
added support for that one as well later; the Bourne shell never had local scope (except of course via subshells))The
local
builtin AFAIK was added first to the Almquist shell (used in BSDs, dash, busybox sh) in 1989, but works significantly differently fromksh
'stypeset
.ash
derivatives don't supporttypeset
as an alias tolocal
, but you can always define one by hand.bash and zsh added
typeset
aliased tolocal
in 1989 and 1991 respectively.ksh88 added
local
as an undocumented alias totypeset
circa 1990 and pdksh and its derivatives in 1994.posh
(based onpdksh
) removedtypeset
(for strict compliance to the Debian Policy that requireslocal
, but nottypeset
).POSIX initially objected to specifying
typeset
on the ground that it was dynamic scoping. So ksh93 (a rewrite of ksh in 1993 by David Korn) switched to static scoping instead. Also in ksh93, as opposed to ksh88, local scoping is only done for functions declared with theksh
syntax (function f {...}
), not the Bourne syntax (f() {...}
) and thelocal
alias was removed.However the ksh93v- beta and final version from AT&T can be compiled with an experimental "bash" mode (actually enabled by default) that does dynamic scoping (in bother forms of functions, including with
local
andtypeset
) whenksh93
is invoked asbash
.local
differs fromtypeset
in that case in that it can only be invoked from within a function. Thatbash
mode will be disabled by default in ksh2020 though thelocal
/declare
aliases totypeset
will be retained even when the bash mode is not compiled in (though still with static scoping).yash
(written much later), hastypeset
(à la ksh88), but has only hadlocal
as an alias to it since version 2.48 (December 2018).@Schily maintains a Bourne shell descendant which has been recently made mostly POSIX compliant, called
bosh
that supports local scope since version 2016-07-06 (withlocal
, similar toash
).So the Bourne-like shells that have some form of local scope for variables today are:
As far as the
sh
of different systems go, note that there are systems where the POSIXsh
is in/bin
(most), and others where it's not (like Solaris where it's in/usr/xpg4/bin
). For thesh
implementation on various systems we have:Now, where they differ:
typeset
(ksh, pdksh, bash, zsh, yash) vslocal
(ksh88, pdksh, bash, zsh, ash, yash 2.48+).function f {...}
function), vs dynamic scoping (all other shells). For instance, whetherfunction f { typeset v=1; g; echo "$v"; }; function g { v=2; }; f
outputs1
or2
. See also how theexport
attribute affects scoping inksh93
.local
/typeset
just makes the variable local (ash
,bosh
), or creates a new instance of the variable (other shells). For instance, whetherv=1; f() { local v; echo "${v:-empty}"; }; f
outputs1
orempty
(see also thelocalvar_inherit
option in bash 5.0 and above).export
) and/or type and which ones from the variable in the parent scope. For instance, whetherexport V=1; f() { local V=2; printenv V; }; f
prints1
,2
or nothing.zsh
) or is initially unset.unset V
on a variable in a local scope leaves the variableunset
, or just peels one level of scoping (mksh
,yash
,bash
under some circumstances). For instance, whetherv=1; f() { local v=2; unset v; echo "$v"; }
outputs1
or nothing (see also thelocalvar_unset
option in bash 5.0 and above)export
, whether it's a keyword or only a mere builtin or both and under what condition it's considered as a keyword.export
, whether the arguments are parsed as normal command arguments or as assignments (and under what condition).v=value myfunction
wheremyfunction
itself declaresv
as local or not.That's the ones I'm thinking of just now. Check the austin group bug above for more details.
As far as local scoping for shell options (as opposed to variables), shells supporting it are:
ksh88
(with both function definition syntax): done by default, I'm not aware of any way to disable it.ash
(since 1989): withlocal -
. It makes the$-
parameter (which stores the list of options) local.ksh93
: now only done forfunction f {...}
functions.zsh
(since 1995). Withsetopt localoptions
. Also withemulate -L
for the emulation mode (and its set of options) to be made local to the function.bash
(since 2016) withlocal -
like inash
.¹ the POSIX
sh
on Solaris is/usr/xpg4/bin/sh
(though it has many conformance bugs including those options local to functions)./bin/sh
up to Solaris 10 was the Bourne shell (so no local scope), and since Solaris 11 is ksh93