zsh和mksh在哪里与bash不兼容?


11

其他POSIX兼容外壳在多大程度上可以作为bash的合理替代品?它们不需要是真正的“即插即用”替换,但是足够接近以使用大多数脚本并通过一些修改来支持其余脚本。

  1. 我想要显式的bash脚本-初始化脚本,DHCP客户端脚本等-只需最少的修改即可工作

  2. 我希望我自己的更专业的shell脚本集合不需要太多修改

  3. 我想拥有诸如字符串操作和内置正则表达式模式匹配之类的功能

我所知道的唯一认真的竞争者是zsh和mksh。因此,对于你们中的一个或两个都擅长的人:

  1. bash分别具有zsh和mksh没有的功能?

  2. shell与bash共享哪些功能,但使用不兼容的语法呢?


3
命令Shell的比较:(en.wikipedia.org/wiki/Comparison_of_command_shells)Bash脚本高级指南:(tldp.org/LDP/abs/html)ZSH手册:(zsh.sourceforge.net/Guide/zshguide.html)MirBSD Shell手册:(mirbsd.org/htman/i386/man1/mksh.htm)。抱歉,这个问题太复杂了。也许您应该问如何为特定的Linux发行版修补bash以修复该漏洞?
Tyler Maginnis 2014年

3
没有一个外壳可以代替替换bash,mksh和zsh可以/bin/sh具有不同级别的正确性,但不能bash
llua 2014年

1
整个冲击波事件似乎与核心问题无关,只是分散了人们的注意力,所以我将其删除了
Michael Mrozek

3
整个问题过于笼统。也许您应该删除它。
Tyler Maginnis 2014年

1
我真正关心的唯一一点是BASH由自由软件基金会(FSF)维护得很好。我在那里的联系人告诉我,BASH中“基本缺陷”的揭示只会导致更强大,更安全的实现。有很多可供选择的外壳程序,即使开始推荐一个适合您的需求,我也需要对您的用例有扎实的了解。然后在O / S方面...与Bash有很多联系,您可能最终将整个O / S调用重写为BASH。我能看到的最好的解决方案是选择一个带有Alt-Shell维护的发行版。
Tyler Maginnis

Answers:


19

我将坚持使用脚本功能。丰富的交互功能(命令行版本,完成,提示等)往往会大相径庭,以完全不兼容的方式实现相似的效果。zsh中有哪些功能,bash中缺少哪些功能,反之亦然?给出了一些交互式使用的指示。

最接近bash的是ATT ksh93mksh(Korn Shell和克隆)。Zsh也具有功能的子集,但是您需要以ksh仿真模式而不是zsh纯模式运行它。

我不会列出POSIX功能(可在任何现代sh外壳程序中使用),相对晦涩的功能,也不会列出用于交互式使用的上述功能。从Debian wheezy上发现,bash 4.2,ksh 93u和mksh 40.9.20120630的观察结果有效。

Shell语法

报价单

$'…'(带有反斜杠插值的文字字符串)在ksh93和mksh中可用。$“…”(翻译后的字符串)特定于bash。

条件构造

Mksh和ksh93必须;&case声明中使用,但不能;;&测试后续案例。Mksh具有;|此功能,最近的mksh允许;;&兼容。

((…))算术表达式和[[ … ]]测试是ksh的功能。一些条件运算符有所不同,请参见下面的“条件表达式”。

协同处理

Ksh和bash都有协同处理,但是它们的工作方式不同。

职能

Mksh和ksh93除支持function name {…}标准外name () {…},还支持函数定义的语法,但function在ksh中使用时会更改作用域规则,因此name () …要保持兼容性。函数名称中允许使用的字符的规则各不相同;坚持字母数字和_

支撑扩展

Ksh93和mksh支持大括号扩展{foo,bar}{1..42}Ksh93 支持数字范围,但mksh不支持。

参数扩展

ksh93的与mksh支持子提取${VAR:offset}${VAR:offset:length},但不区分折叠状${VAR^}${VAR,}等等。你可以做大小写转换typeset -l,并typeset -u在两者的bash和ksh。

他们支持用${VAR/PATTERN/STRING}或替换${VAR/PATTERN//STRING}。STRING的引用规则略有不同,因此请在STRING中避免反斜杠(可能还有其他字符)(构建变量,${VAR/PATTERN/$REPLACEMENT}如果替换包含引号字符,则使用反斜杠)。

阵列扩展(${ARRAY[KEY]}"${ARRAY[@]}"${#ARRAY[@]}${!ARRAY[@]}在bash)工作像ksh的。

${!VAR}扩展到is ${OTHERVAR}的值(间接变量引用)特定于bash的情况(ksh与有所不同)。要在ksh中实现这种双重扩展,您需要使用名称引用()。工作原理相同。VAROTHERVAR${!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特有的功能(其它炮弹从来没有做到这一点的脚本)。

<<< 这里的字符串在ksh中的作用就像在bash中一样。

>&mksh也支持重定向语法错误的快捷方式,但ksh93不支持。

条件表达式

[[ … ]] 双括号语法

像bash一样,ATT ksh93和mksh都支持ksh的双括号语法。

文件运算符

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的。你可以得到一个类似的效果.sh.fun.sh.file.sh.lineno在ksh93的。在mksh中,最后有一个LINENO

declarelocaltypeset

declare是ksh的bash专用名称typeset。使用typeset:它也可以在bash中使用。

Mksh定义local为的别名typeset。在ksh93中,您需要使用typeset(或定义一个别名)。

Mksh没有关联数组(它们计划用于尚未发布的版本)。

我不认为typeset -tksh中有bash的(跟踪函数)的完全等效项。

cd

Ksh93没有-e

echo

Ksh93和mksh处理bash 中的-e-n选项。Mksh也了解-E,ksh93不会将其视为选项。在ksh93中,默认情况下,反斜杠扩展处于关闭状态;在mksh中,默认情况下处于启用状态。

enable

Ksh没有提供禁用内置命令的方法。为避免内置,请查找外部命令的路径并显式调用它。

exec

Ksh93有-a但没有-l。Mksh都没有。

export

ksh93和mksh都没有export -ntypeset +x foo改为使用,它可以在bash和ksh中使用。

Ksh不会通过环境导出功能。

let

let 在bash和ksh中是相同的。

mapfilereadarray

这是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的所有选项。选项-r-d-n-N-t-u是在bash,ksh93的和mksh相同。

readonly

您可以使用相同的语法在Ksh93和mksh中将变量声明为只读。如果变量是数组,则需要首先分配给它,然后使用将其设为只读readonly VAR。在ksh中不能将函数设为只读。

setshopt

所有的选项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 —不要在遍历中忽略点文件

要模拟dotglobksh93中的选项,可以设置FIGNORE='@(.|..)'。我认为mksh中没有这样的东西。

shopt -s extglob — ksh扩展的glob模式

extglob选项实际上始终在ksh中启用。

shopt -s failglob —如果全局模式不匹配,则会出错

我认为这在mksh或ksh93中都不存在。它在zsh中(默认行为,除非null_globcsh_null_glob设置)。

shopt -s globstar—  **/递归遍历

Ksh93具有递归glob **/,已启用set -G。Mksh没有递归glob。

shopt -s lastpipe —在父外壳中运行管道的最后一条命令

Ksh93始终在父外壳程序中运行管道的最后一个命令,在bash中,这需要设置lastpipe选项。Mksh始终在子Shell中运行管道的最后一个命令。

shopt -s nocaseglobshopt -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的。

Mksh和ksh93都在ENV启动时提供给定的文件。

EUID并且UID在ksh93中不存在。Mksh称他们USER_IDKSH_UID; 它没有GROUPS

FUNCNAME并且FUNCNEST在ksh中不存在。Ksh93具有.sh.fun.sh.level。用function foo { …; }(没有括号!)声明的函数在中具有自己的名称$0

GLOBIGNORE在ksh93中存在,但具有不同的名称和语法:称为FIGNORE,它是单个模式,而不是用冒号分隔的列表。使用@(…|…)模式。Ksh FIGNORE包含bash的语法完全不同。

Ksh93和mksh没有类似的东西HOSTTYPEMACHTYPE并且OSTYPE。也SHELLOPTS还是TIMEFORMAT

Mksh有PIPESTATUS,但ksh93没有。

Mksh和ksh93具有RANDOM


如果您不想在句子的开头正确地复制(未注册的)商标“ mksh”(另请参见此处的“ dd。”与“ dd”。),请输入“ The MirBSD” Korn Shell”。它总是错误的大写“mksh”中的任何字母。谢谢。
mirabilos 2014年

怎么可以$BASHPID模仿sh -c 'echo $PPID'?我(sh -c 'echo $PPID')在Bash中尝试过,但是它给了我与相同的PID $$
富兰克林·于

我认为这个问题更适合这里。我将问题复制到那里。
富兰克林·于

4

这个问题太笼统了。

无论mkshzsh的是支持了大量的炮弹GNU bash的特异性扩展,但总有一些不被理解。

zsh支持更多功能,但仅以其本机zsh模式支持,该模式与POSIX shell(例如GNU bash,AT&T ksh93,mksh)不兼容。而且,mksh更精简,更快,更便携。

通常,如果这是我们正在讨论脚本,请继续进行测试。(mksh尚不支持bash4样式的关联数组。“ declare”命令是bash特定的,“ typeset”是等效的。我对zsh不够了解,无法直接声明任何有关此内容的信息。ksh93没有“ local”但也使用“类型集”。)但是,如果这是关于运行无bash的Debian系统,那就算了。bash的存在是“承诺”(系统的API / ABI)的一部分,并且在很大程度上依赖于此。

免责声明:我是mksh开发人员。


1

ZSH壳比较

近年来,扩展中存在一定数量的交叉。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,所以我不知道在哪里寻找答案。

如果您正在寻找外壳的安全替代品,那么就您所讨论的固有缺陷而言,这些外壳与Bash的区别都不大。

像Perl这样的语言可以更安全地处理输入。但是可维护性在这里也很关键。Perl Shell替换不是很理想。Shell维护人员有责任安全地处理输入。因此,在编写脚本时,请验证,验证和验证所有内容!使用代码合同来确保每次都能得到正确的结果!

Perl Shell

FSF关于壳牌冲击的声明


如果确实是其他外壳程序具有相同的缺陷(基于我所看到的讨论,我对此有所怀疑),那么这是一个巨大的问题,因为许多第三方程序将变量无任何形式地传递给外壳程序验证或其他任何东西。如果是这样的话,我们不妨完全抛弃POSIX。
DanL4096

1
Bash项目在10年中有1次0天漏洞利用。Microsoft Windows每周都有0天以上的10个漏洞利用。您甚至不知道有多少人为此困扰了Bash开发团队?只需更新它并继续前进即可!没什么大不了的。
Tyler Maginnis 2014年

1
@ DanL4096。将变量传递给随后没有对变量进行充分检查的shell脚本,就没有理由抛弃POSIX。问题在于首先编写shell脚本的人。
fpmurphy 2014年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.