I'll stick to scripting features. Rich interactive features (command line edition, completion, prompts, etc.) tend to be very different, achieving similar effects in wholly incompatible ways. What features are in zsh and missing from bash, or vice versa? gives a few pointers on interactive use.
The closest thing to bash would be ATT ksh93 or mksh (the Korn shell and a clone). Zsh also has a subset of features but you would need to run it in ksh emulation mode, not in zsh native mode.
I won't list POSIX features (which are available in any modern sh
shell), nor relatively obscure features, nor as mentioned above features for interactive use. Observations are valid as of bash 4.2, ksh 93u and mksh 40.9.20120630 as found on Debian wheezy.
$'…'
(literal strings with backslash interpolation) is available in ksh93 and mksh. `$"…" (translated strings) is bash-specific.
Mksh and ksh93 have ;&
to fall through in a case
statement, but not ;;&
to test subsequent cases. Mksh has ;|
for that, and recent mksh allows ;;&
for compatibility.
((…))
arithmetic expressions and [[ … ]]
tests are ksh features. Some conditional operators are different, see “conditional expressions” below.
Ksh and bash both have coprocesses but they work differently.
Mksh and ksh93 support the function name {…}
syntax for function definitions in addition to the standard name () {…}
, but using function
in ksh changes scoping rules, so stick to name () …
to maintain compatibility. The rules for allowed characters in function names vary; stick to alphanumerics and _
.
Ksh93 and mksh support brace expansion {foo,bar}
. Ksh93 supports numeric ranges {1..42}
but mksh doesn't.
Ksh93 and mksh support substring extraction with ${VAR:offset}
and ${VAR:offset:length}
, but not case folding like ${VAR^}
, ${VAR,}
, etc. You can do case conversion with typeset -l
and typeset -u
in both bash and ksh.
They support replacement with ${VAR/PATTERN/STRING}
or ${VAR/PATTERN//STRING}
. The quoting rules for STRING are slightly different, so avoid backslashes (and maybe other characters) in STRING (build a variable and use ${VAR/PATTERN/$REPLACEMENT}
instead if the replacement contains quoting characters).
Array expansion (${ARRAY[KEY]}
, "${ARRAY[@]}"
, ${#ARRAY[@]}
, ${!ARRAY[@]}
) work in bash like in ksh.
${!VAR}
expanding to ${OTHERVAR}
when the value of VAR
is OTHERVAR
(indirect variable reference) is bash-specific (ksh does something different with ${!VAR}
). To get this double expansion in ksh, you need to use a name reference instead (typeset -n VAR=OTHERVAR; echo "$VAR"
). ${!PREFIX*}
works the same.
Process substitution <(…)
and >(…)
is supported in ksh93 but not in mksh.
The ksh extended glob patterns that need shopt -s extglob
to be activated in bash are always available in ksh93 and mksh.
Mksh doesn't support character classes like [[:alpha:]]
.
Bash and ksh93 define pseudo-files /dev/tcp/HOST/PORT
and /dev/udp/HOST/PORT
, but mksh doesn't.
Expanding wildcards in a redirection in scripts (as in var="*.txt"; echo hello >$a
writing to a.txt
if that file name is the sole match for the pattern) is a bash-specific feature (other shells never do it in scripts).
<<<
here-strings work in ksh like in bash.
The shortcut >&
to redirect syntax errors is also supported by mksh but not by ksh93.
[[ … ]]
double bracket syntax
The double bracket syntax from ksh is supported by both ATT ksh93 and mksh like in bash.
File operators
Ksh93, mksh and bash support the same extensions to POSIX, including -a
as an obsolete synonym of -e
, -k
(sticky), -G
(owned by egid), -O
(owner by euid), -ef
(same file), -nt
(newer than), -ot
(older than).
-N FILE
(modified since last read) isn't supported by mksh.
Mksh doesn't have a regexp matching operator =~
. Ksh93 has this operator, and it performs the same matching as in bash, but doesn't have an equivalent of BASH_REMATCH
to retrieve matched groups afterwards.
String operators
Ksh93 and mksh support the same string comparison operators <
and >
as bash as well as the ==
synonym of =
. Mksh doesn't use locale settings to determine the lexicographic order, it compares strings as byte strings.
Other operators
-v VAR
to test if a variable is defined is bash-specific. In any POSIX shell, you can use [ -z "${VAR+1}" ]
.
The set of allowed character in alias names isn't the same in all shells. I think it's the same as for functions (see above).
Ksh93 has a builtin called builtin
, but it doesn't execute a name as a built-in command. Use command
to bypass aliases and functions; this will call a builtin if one exists, otherwise an external command (you can avoid this with PATH= command error_out_if_this_is_not_a_builtin
).
This is bash-specific. You can get a similar effect with .sh.fun
, .sh.file
and .sh.lineno
in ksh93. In mksh there's at last LINENO
.
declare
is a bash-specific name for ksh's typeset
. Use typeset
: it also works in bash.
Mksh defines local
as an alias for typeset
. In ksh93, you need to use typeset
(or define an alias).
Mksh has no associative arrays (they're slated for an as yet unreleased version).
I don't think there's an exact equivalent of bash's typeset -t
(trace function) in ksh.
Ksh93 doesn't have -e
.
Ksh93 and mksh process the -e
and -n
options like in bash. Mksh also understands -E
, ksh93 doesn't treat it as an option. Backslash expansion is off by default in ksh93, on by default in mksh.
Ksh doesn't provide a way to disable builtin commands. To avoid a builtin, look up the external command's path and invoke it explicitly.
Ksh93 has -a
but not -l
. Mksh has neither.
Neither ksh93 nor mksh has export -n
. Use typeset +x foo
instead, it works in bash and ksh.
Ksh doesn't export functions through the environment.
let
is the same in bash and ksh.
This is a bash-specific feature. You can use while read
loops or command substitution to read a file and split it into an array of lines. Take care of IFS
and globbing. Here's the equivalent of mapfile -t lines </path/to/file
:
IFS=$'\n'; set -f
lines=($(</path/to/file))
unset IFS; set +f
printf
is very similar. I think ksh93 supports all of bash's format directives. mksh doesn't support %q
or %(DATE_FORMAT)T
; on some installations, printf
isn't an mksh builtin and calls the external command instead.
printf -v VAR
is bash-specific, ksh always prints to standard output.
Several options are bash-specific, including all the ones about readline. The options -r
, -d
, -n
, -N
, -t
, -u
are identical in bash, ksh93 and mksh.
You can declare a variable as read-only in Ksh93 and mksh with the same syntax. If the variable is an array, you need to assign to it first, then make it read-only with readonly VAR
. Functions can't be made read-only in ksh.
All the options to set
and set -o
are POSIX or ksh features.
shopt
is bash-specific. Many options concern interactive use anyway. For effects on globbing and other features enabled by some options, see the section “Options” below.
This variant of .
exists in ksh as well. In bash and mksh, source
searches the current directory after PATH
, but in ksh93, it's an exact equivalent of .
.
The DEBUG
pseudo-signal isn't implemented in mksh. In ksh93, it exists with a different way to report information, see the manual for details.
In ksh, type
is an alias for whence -v
. In mksh, type -p
does not print the path to the executable, but a human-readable message; you need to use whence -p COMMAND
instead.
Options
shopt -s dotglob
— don't ignore dot files in globbing
To emulate the dotglob
option in ksh93, you can set FIGNORE='@(.|..)'
. I don't think there's anything like this in mksh.
The extglob
option is effectively always on in ksh.
shopt -s failglob
— error out if a glob pattern matches nothing
I don't think this exists in either mksh or ksh93. It does in zsh (default behavior unless null_glob
or csh_null_glob
are set).
Ksh93 has recursive globbing with **/
, enabled with set -G
. Mksh doesn't have recursive globbing.
shopt -s lastpipe
— run the last command of a pipeline in the parent shell
Ksh93 always runs the last command of a pipeline in the parent shell, which in bash requires the lastpipe
option to be set. Mksh always runs the last command of a pipeline in a subshell.
shopt -s nocaseglob
, shopt -s nocasematch
— case-insensitive patterns
Mksh doesn't have case-insensitive pattern matching. Ksh93 supports it on a pattern-by-pattern basis: prefix the pattern with ~(i)
.
shopt -s nullglob
— expand patterns that match no file to an empty list
Mksh doesn't have this. Ksh93 supports it on a pattern-by-pattern basis: prefix the pattern with ~(N)
.
Obviously most of the BASH_xxx
variables don't exist in ksh. $BASHPID
can be emulated with the costly but portable sh -c 'echo $PPID'
, and has been recently added to mksh. BASH_LINE
is .sh.lineno
in ksh93 and LINENO
in mksh. BASH_SUBSHELL
is .sh.subshell
in ksh93.
Mksh and ksh93 both source the file given in ENV
when they start up.
EUID
and UID
don't exist in ksh93. Mksh calls them USER_ID
and KSH_UID
; it doesn't have GROUPS
.
FUNCNAME
and FUNCNEST
don't exist in ksh. Ksh93 has .sh.fun
and .sh.level
. Functions declared with function foo { …; }
(no parentheses!) have their own name in $0
.
GLOBIGNORE
exists in ksh93 but with a different name and syntax: it's called FIGNORE
, and it's a single pattern, not a colon-separated list. Use a @(…|…)
pattern. Ksh's FIGNORE
subsumes bash's, with a wholly different syntax.
Ksh93 and mksh have nothing like HOSTTYPE
, MACHTYPE
and OSTYPE
. Nor SHELLOPTS
or TIMEFORMAT
.
Mksh has PIPESTATUS
, but ksh93 doesn't.
Mksh and ksh93 have RANDOM
.
bash
, mksh and zsh can function as/bin/sh
with various levels of correctness, but notbash
. – llua Oct 02 '14 at 16:32There are so many alternative shells that even beginning to recommend one for your requirement, I would require a solid understanding of your use case. Then on the O/S side... So many things are tied to Bash that you may end up rewriting your entire O/S calls to BASH. The best solution I can see is to choose a distribution with a maintained alt-shell.
– Tyler Maginnis Oct 02 '14 at 18:25