其他 POSIX 兼容 shell 在多大程度上可以作为 bash 的合理替代品?它们不需要是真正的“直接”替代品,但足够接近以进行合作最多脚本并通过一些修改支持其余部分。
我想要显式的 bash 脚本 - initscripts、DHCP 客户端脚本等 - 只需进行最少的修改即可工作
我希望我自己收集的更专业的 shell 脚本不需要太多修改
我想要具有字符串操作和内置正则表达式模式匹配等功能
据我所知,唯一有力的竞争者是 zsh 和 mksh。因此,对于那些擅长其中一个或两个的人:
bash 有哪些功能是 zsh 和 mksh 分别没有的?
shell 与 bash 共享哪些功能,但使用不兼容的语法?
答案1
我将坚持使用脚本功能。丰富的交互功能(命令行编辑、完成、提示等)往往差异很大,以完全不兼容的方式实现相似的效果。zsh 中有哪些功能是 bash 缺少的,反之亦然?给出了一些关于交互使用的指示。
最接近 bash 的是ATT ksh93或者姆克什(Korn shell 和克隆)。 Zsh 也有一部分功能,但您需要在 ksh 模拟模式下运行它,而不是在 zsh 本机模式下运行。
我就不列出来了POSIX功能(在任何现代 shell 中都可用sh
),也不是相对晦涩的功能,也不是上面提到的用于交互使用的功能。从 Debian wheezy 上发现的 bash 4.2、ksh 93u 和 mksh 40.9.20120630 开始,观察结果有效。
外壳语法
引用
$'…'
(带有反斜杠插值的文字字符串)在 ksh93 和 mksh 中可用。 `$"..." (翻译后的字符串)是 bash 特定的。
条件构造
mksh和ksh93;&
必须在声明中失败case
,但不能;;&
测试后续案例。 Mksh 已经做到了;|
这一点,并且最近的 mksh 允许;;&
兼容性。
((…))
算术表达式和[[ … ]]
测试是 ksh 的功能。有些条件运算符有所不同,请参阅下面的“条件表达式”。
协进程
Ksh 和 bash 都有协进程,但它们的工作方式不同。
功能
Mksh 和 ksh93function name {…}
除了标准之外还支持函数定义的语法name () {…}
,但function
在 ksh 中使用会更改作用域规则,因此请坚持name () …
保持兼容性。函数名称中允许使用的字符的规则各不相同;坚持使用字母数字和_
.
支撑扩张
Ksh93 和 mksh 支持大括号扩展{foo,bar}
。 Ksh93 支持数字范围{1..42}
,但 mksh 不支持。
参数扩展
Ksh93 和 mksh 支持使用${VAR:offset}
and提取子字符串${VAR:offset:length}
,但不支持大小写折叠,如${VAR^}
,${VAR,}
等。您可以在 bash 和 ksh 中使用typeset -l
and进行大小写转换。typeset -u
他们支持用${VAR/PATTERN/STRING}
或替换${VAR/PATTERN//STRING}
。 STRING 的引用规则略有不同,因此请避免在 STRING 中使用反斜杠(也可能是其他字符)(构建一个变量,${VAR/PATTERN/$REPLACEMENT}
如果替换包含引用字符,则改为使用)。
数组扩展 ( ${ARRAY[KEY]}
, "${ARRAY[@]}"
, ${#ARRAY[@]}
, ${!ARRAY[@]}
) 在 bash 中的工作方式与在 ksh 中一样。
${!VAR}
扩展到is (间接变量引用)${OTHERVAR}
的值是特定于 bash 的(ksh 做了一些与 不同的事情)。要在 ksh 中获得这种双重扩展,您需要使用名称引用 ( )。工作原理是一样的。VAR
OTHERVAR
${!VAR}
typeset -n VAR=OTHERVAR; echo "$VAR"
${!PREFIX*}
工艺替代
ksh93 支持进程替换<(…)
,>(…)
但 mksh 不支持。
通配符模式
shopt -s extglob
需要在 bash 中激活的ksh 扩展 glob 模式在 ksh93 和 mksh 中始终可用。
Mksh 不支持像[[:alpha:]]
.
IO重定向
Bash 和 ksh93 定义了伪文件和,但 mksh 没有。/dev/tcp/HOST/PORT
/dev/udp/HOST/PORT
在脚本中的重定向中扩展通配符(如var="*.txt"; echo hello >$a
写入a.txt
文件名是否是模式的唯一匹配)是 bash 特定的功能(其他 shell 从不在脚本中执行此操作)。
<<<
这里的字符串在 ksh 中工作就像在 bash 中一样。
>&
mksh 也支持重定向语法错误的快捷方式,但 ksh93 不支持。
条件表达式
[[ … ]]
双括号语法
ATT ksh93 和 mksh 都支持 ksh 中的双括号语法,就像 bash 中一样。
文件操作符
Ksh93、mksh 和 bash 支持相同的 POSIX 扩展,包括作为、(粘性)、(由egid 拥有)、(由 euid 拥有)、(相同文件)、(比新)、(比)-a
的过时同义词。-e
-k
-G
-O
-ef
-nt
-ot
-N FILE
(自上次阅读以来已修改)mksh 不支持。
Mksh 没有正则表达式匹配运算符=~
。 Ksh93 有这个运算符,它执行与 bash 中相同的匹配,但没有等效的BASH_REMATCH
来检索匹配的组。
字符串运算符
Ksh93 和 mksh 支持与 bash相同的字符串比较运算符<
以及. Mksh 不使用区域设置来确定字典顺序,它将字符串作为字节字符串进行比较。>
==
=
其他运营商
-v VAR
测试变量是否定义为 bash 特定的。在任何 POSIX shell 中,您都可以使用[ -z "${VAR+1}" ]
.
内置函数
alias
在所有 shell 中,别名中允许的字符集并不相同。我认为这与函数相同(见上文)。
builtin
Ksh93 有一个名为 的内置命令builtin
,但它不会将名称作为内置命令执行。用于command
绕过别名和函数;如果存在内置命令,这将调用内置命令,否则将调用外部命令(您可以使用 避免这种情况PATH= command error_out_if_this_is_not_a_builtin
)。
caller
这是 bash 特定的。在 ksh93 中,您可以使用.sh.fun
,.sh.file
和获得类似的效果。 .sh.lineno
mksh 终于有LINENO
.
declare
,local
,typeset
declare
是 ksh 的 bash 特定名称typeset
。使用typeset
:它也适用于 bash。
Mksh 定义local
为 的别名typeset
。在ksh93中,需要使用typeset
(或定义别名)。
Mksh 没有关联数组(它们预计用于尚未发布的版本)。
typeset -t
我不认为ksh 中存在与 bash(跟踪函数)完全相同的功能。
cd
Ksh93 没有-e
.
echo
Ksh93 和 mksh 处理-e
和-n
选项就像在 bash 中一样。 mksh也明白-E
,ksh93并不把它当作一个选项。反斜杠扩展在 ksh93 中默认关闭,在 mksh 中默认打开。
enable
Ksh 不提供禁用内置命令的方法。为了避免使用内置命令,请查找外部命令的路径并显式调用它。
exec
Ksh93 有-a
但没有-l
。 Mksh 两者都没有。
export
ksh93 和 mksh 都没有export -n
.改用typeset +x foo
它,它可以在 bash 和 ksh 中工作。
Ksh 不通过环境导出函数。
let
let
bash 和 ksh 中是相同的。
mapfile
,readarray
这是 bash 特定的功能。您可以使用while read
循环或命令替换来读取文件并将其拆分为行数组。照顾IFS
和通配。这相当于mapfile -t lines </path/to/file
:
IFS=$'\n'; set -f
lines=($(</path/to/file))
unset IFS; set +f
printf
printf
非常相似。我认为 ksh93 支持 bash 的所有格式指令。 mksh 不支持%q
或%(DATE_FORMAT)T
;在某些安装上,printf
不是 mksh 内置命令,而是调用外部命令。
printf -v VAR
是特定于 bash 的,ksh 总是打印到标准输出。
read
有几个选项是 bash 特定的,包括所有有关 readline 的选项。 bash、ksh93 和 mksh 中的选项-r
, -d
, -n
, -N
, -t
,-u
相同。
readonly
您可以使用相同的语法在 Ksh93 和 mksh 中将变量声明为只读。如果变量是数组,则需要先对其进行赋值,然后使用 使其只读readonly VAR
。 ksh 中的函数无法设为只读。
set
,shopt
set
和的所有选项set -o
都是 POSIX 或 ksh 功能。
shopt
是 bash 特定的。无论如何,许多选项都涉及交互使用。有关某些选项对通配符和其他功能的影响,请参阅下面的“选项”部分。
source
.
ksh 中也存在这种变体。在 bash 和 mksh 中,source
在 后搜索当前目录PATH
,但在 ksh93 中,它与.
.
trap
DEBUG
mksh 中未实现伪信号。在ksh93中,它以不同的方式报告信息,详细信息请参阅手册。
type
在 ksh 中,type
是 的别名whence -v
。在 mksh 中,type -p
不打印可执行文件的路径,而是打印人类可读的消息;你需要改用whence -p COMMAND
。
选项
shopt -s dotglob
- 不要忽略通配中的点文件
要模拟dotglob
ksh93 中的选项,您可以设置FIGNORE='@(.|..)'
.我不认为 mksh 中有这样的东西。
shopt -s extglob
— ksh 扩展 glob 模式
该extglob
选项在 ksh 中始终有效。
shopt -s failglob
— 如果 glob 模式不匹配任何内容,则会出错
我认为 mksh 或 ksh93 中都不存在这种情况。它在 zsh 中执行(默认行为,除非设置null_glob
或)。csh_null_glob
shopt -s globstar
— **/
递归通配符
Ksh93 具有递归通配符**/
,可通过 启用set -G
。 Mksh 没有递归通配符。
shopt -s lastpipe
— 在父 shell 中运行管道的最后一个命令
Ksh93 始终在父 shell 中运行管道的最后一个命令,这在 bash 中需要lastpipe
设置该选项。 Mksh 始终在子 shell 中运行管道的最后一个命令。
shopt -s nocaseglob
, shopt -s nocasematch
— 不区分大小写的模式
Mksh 没有不区分大小写的模式匹配。 Ksh93 在逐个模式的基础上支持它:在模式前加上~(i)
.
shopt -s nullglob
— 将没有文件匹配的模式扩展为空列表
Mksh没有这个。 Ksh93 在逐个模式的基础上支持它:在模式前加上~(N)
.
变量
显然大多数BASH_xxx
变量在 ksh 中不存在。$BASHPID
可以用昂贵但可移植的 来模拟sh -c 'echo $PPID'
,并且最近已添加到 mksh 中。BASH_LINE
位于.sh.lineno
ksh93 和LINENO
mksh 中。BASH_SUBSHELL
位于.sh.subshell
ksh93 中。
ENV
Mksh 和 ksh93 都获取启动时给出的文件。
EUID
并且UID
在 ksh93 中不存在。 Mksh 称他们USER_ID
为KSH_UID
;它没有GROUPS
。
FUNCNAME
并且FUNCNEST
在 ksh 中不存在。 Ksh93 有.sh.fun
和.sh.level
。用function foo { …; }
(没有括号!)声明的函数在$0
.
GLOBIGNORE
存在于 ksh93 中,但具有不同的名称和语法:它称为FIGNORE
,并且它是单个模式,而不是冒号分隔的列表。使用@(…|…)
模式。 KshFIGNORE
包含了 bash,但语法完全不同。
Ksh93 和 mksh 没有HOSTTYPE
,MACHTYPE
和 之类的东西OSTYPE
。也不SHELLOPTS
或TIMEFORMAT
。
Mksh 有PIPESTATUS
,但 ksh93 没有。
Mksh 和 ksh93 有RANDOM
.
答案2
这个问题太宽泛了。
两个都姆克什和桀骜是支持很多的shellGNU bash-特定的扩展,但总有一些不理解。
zsh 支持更多东西,但仅限于其本机 zsh 模式,该模式与 POSIX shell(例如 GNU bash、AT&T ksh93,mksh)。此外,mksh 更精简、更快、更便携。
一般来说,如果这是你的我们正在谈论的脚本,继续,测试它们。 (mksh 还不支持 bash4 风格的关联数组。“declare”命令是 bash 特定的,“typeset”是等价的。我对 zsh 不太熟悉,无法直接说明任何相关内容。ksh93 没有“local”但也使用“typeset”。)但是,如果这是关于运行一个无 bash 的 Debian 系统,那就算了。 bash 的存在是“承诺”(系统的 API/ABI)的一部分,并且很大程度上依赖于它。
免责声明:我是 mksh 开发人员。
答案3
然而,近年来,扩展中出现了一定程度的交叉。 Zsh(从 3.1.6 开始)具有 bash 的
${var/old/new}' feature for replacing the text old with the text new in the parameter $var. Note one difference here: while both shells implement the syntax
${var/#old/new}' 和${var/%old/new}' for anchoring the match of old to the start or end of the parameter text, respectively, in zsh you can't put the
#' 或%' inside a parameter: in other words
{var/$old/new}',其中 old 以#' treats that as an ordinary character in zsh, unlike bash. To do this sort of thing in zsh you can use (from 3.1.7) the new syntax for anchors in any pattern,
(#s)' 开头以匹配字符串的开头,和“(#e)”来匹配结尾。这些需要设置选项 EXTENDED_GLOB。
我对 mksh 最不熟悉,所以我不知道在哪里寻找答案。
如果您正在寻找 shell 的安全替代品,就您所讨论的固有缺陷而言,这些 shell 与 Bash 没有太大区别。
像 Perl 这样的语言可以更安全地处理输入。但可维护性也是这里的关键。 Perl shell 替代品并未得到很好的采用。安全地处理输入是 shell 维护者的责任。所以当你写脚本的时候,验证,验证,验证一切!使用代码契约来确保每次都得到正确的结果!
答案4
一项功能不是默认启用mksh
是 shell 的历史。
在你
.mkshrc
刚刚设置的:export HISTFILE=~/.mksh-history