在 Bash 中,有两个内置命令用来控制 Bash 的各种可配置行为的开关(打开或关闭),这些开关称之为选项(option)。其中一个命令是 set,set 命令有三种功能:显示所有的变量和函数;修改 Bash 的位置参数;控制 Bash 的第一套选项。可见 set 命令完全违背了“一个命令只干一件事”的 UNIX 哲学。另外一个命令是 shopt,从名字(shell options 的缩写)就可以看出,它的功能是控制 Bash 的另一套选项。那么问题就来了,为啥要用两套选项?

在回答为什么之前,我们先看看两者的不同点:

1. set 命令是 POSIX 规范,shopt 不是

set 命令是 Bash 从 sh 继承来的,而且它和它的大多数选项一起都是在 POSIX 规范中的。而 shopt 是 Bash 在 2.0 版本时新增的,别的 Shell 没有这个命令。

$ set -o | wc -l

27

$ shopt | wc -l

47

在我电脑上的 Bash 4.4 beta 中,set 一共有 27 个选项,shopt 一共有 47 个选项。

2. set 命令和 shopt 命令分别对应两个不同的环境变量

在 Bash 1.* 时代,用 set 命令开启的选项只能在当前 Shell 进程中生效,没有办法通过环境变量传递给它的子进程 Shell,从 Bash 2.0 开始,新增了一个只读变量 SHELLOPTS,只要把它设置成环境变量,它就能把在当前 Shell 中打开的选项传递给子进程 Shell。

$ echo $SHELLOPTS

braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor

$ set -o noglob

$ echo $SHELLOPTS

braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor:noglob

$ echo *

*

$ export SHELLOPTS

$ bash -c 'echo *'

*

上面的例子演示了:在当前 Shell 中打开了 noglob 选项,然后 SHELLOPTS 变量的值会自动同步(所有开启的选项名用冒号 join 成的字符串),但这个变量默认并不是环境变量,需要手动 export 一下,然后子进程 Shell 会获取到这个环境变量的值,解析之后,打开这些继承来的选项。为了演示 Bash 的确有这个解析过程,可以这么玩:

$ env SHELLOPTS=foo bash

bash: foo: invalid option name

值得注意的是,虽然 shopt 命令和 SHELLOPTS 变量是同时实现的(Bash 2.0),而且它俩的名字看起来也的确像是有对应关系似的,然而并没有。shopt 命令一直没有一个像 set 命令之于 SHELLOPTS 的东西,直到 Bash 4.1,才有了 BASHOPTS 变量,它的功能和 SHELLOPTS 一样,用来把 shopt 命令打开的选项传递给子进程 Shell,这里就不具体演示了。

3. shopt 也可以控制 set 的选项,反之则不行

shopt 命令有个 -o 选项,这个选项的功能就是用来查看或修改原本用 set 控制的那套选项,比如我们随便选个 set 的选项 noglob:

$ shopt -s noglob

bash: shopt: noglob: invalid shell option name

$ shopt -so noglob

$ shopt noglob

bash: shopt: noglob: invalid shell option name

$ shopt -o noglob

noglob         on

不加 -o 控制自己的一套选项,加上 -o 控制 set 控制的那套选项。可见在控制 Bash 的选项这个功能上,shopt 命令完全可以代替 set 命令。

4. 为什么要发明 shopt

在了解了这两个命令之后,我不禁要问:为什么要发明一个新的命令?要知道,清楚的记住哪个选项属于哪个命令是很难的,比如我问你 noglob 和 nullglob 哪个是 set 选项哪个是 shopt 选项,没几个人能记得。为什么不像 zsh 一样让 set 管理所有的选项呢:

$ zsh -c 'set -o | wc -l'

176

我自己猜测了很久:是不是 set 命令的短选项不够了?但我又看到不是所有的 set 长选项都有对应的短选项。是不是 Bash 作者在当时决定以后把 POSIX 规定的选项放一个地方,把其它 Bash 私有的选项放另一个地方,况且 set 命令已经很复杂了,所以发明了个新命令?然后我又发现很多 set 的选项都不在 POSIX 规范里,比如 onecmd、pipefail、history 和 errtrace 等。由于这些猜测说服不了我的好奇心,于是我在 help-bash 上询问了 Bash 作者,毕竟这是 20 年前的事了,除了他谁还可能知道 http://lists.gnu.org/archive/html/help-bash/2015-10/msg00008.html

在邮件里,我咨询了两个问题,一个就是“为什么不让 set 控制所有的选项,为什么要发明 shopt”;另外一个是“给 shopt -o 参数是不是意味着 Bash 的实现者鼓励人们用新的 shopt 命令而不是旧的 set 命令来控制 Bash 选项”。

第一个问题的答案比较复杂,总结一下就是:作者的出发点的确是为了让 set 控制“那些在 POSIX 规范里的选项”,以及“那些从 sh 继承来的,但不在 POSIX 规范里的选项(比如上面提到的 onecmd)”,以及“那些为了兼容性,从 ksh 引入的,但不在 POSIX 规范里的选项(比如上面提到的 pipefail)”;让 shopt 控制那些 Bash 私有的选项。但由于历史原因,20 年以后,现在看来,这两个出发点显得都不是那么有说服力:现在的 set 选项里存在着既不是从 sh 继承的,又不是从 ksh 学来的,又不在 POSIX 规范中的选项,比如 history 和 errtrace 等,Bash 作者解释说,history 是他希望 POSIX 规范能采纳(然而目前并没有),所以他先实现在了 Bash 里, errtrace(-E) 选项是因为他为了和 errexit(-e)对应起来,所以实现了,他还说如果再来一次的话,他会把 errtrace 放在 shopt 里。至于 shopt 里放着的选项是不是都是 Bash 私有的,也并不是,ksh 和 zsh 也从这些选项里引入了一些到自己的 set 选项里。除了上面我提到名字的选项,邮件里还讲了另外一些不符合一般规律的选项,很复杂,看了也记不住。总之,两个命令的两套选项显得杂乱无章,毫无规律,是历史原因。读到这里,也许有些好奇心强的朋友还想问:难道把 Bash 的私有选项也放 set 里不行吗,不行吗,不行吗!是行,这只是 Bash 作者在当时做的一个决定,要分开放,没什么特殊的原因,这样说应该说服你了吧。

第二个问题没有回答我,我就不再追问了,我猜答案是肯定的,否则干嘛实现那个功能。

Bash 为何要发明 shopt 命令的更多相关文章

  1. Shopt命令(删除排除)

    有时候我们需要反选某个文件以外的其他文件,就会用到rm -rf!(file)命令,但是有时候这条命令会报错显示 -bash: !: event not found 解决办法:shopt -s extg ...

  2. git 入门教程之 git bash 竟然不支持 tree 命令

    开门见山 git bash 是 Windows 用户安装 git 时默认安装的命令行工具,不仅界面漂亮功能也不错,大多数情况下可以替代 Windows 原生的 cmd 命令行. 然而,git bash ...

  3. Bash 中同名的内部命令和外部命令

    昨天有个人在 bug-bash 上问:为什么 [ --help 没有输出帮助信息.有人回答他了,原因是 coreutils 提供的 [ 命令才接受 --help 选项,Bash 自己的 [ 命令不接受 ...

  4. 单行bash、shell、perl命令

    主题:单行经典bash.shell.perl命令 作者:luomg 摘要: 会陆陆续的写自己工作中的常用有意思的命令,争取你能看完后就能搞定常见操作, 且尽量自少提供基本shell.perl的实现方式 ...

  5. -bash: start-all.sh: 未找到命令

    解决方案:以root权限进入,找到hadoop安装的目录,进入sbin目录下 输入命令#start-all.sh 出现错误:-bash: start-all.sh: 未找到命令 百度了一下:原来需要输 ...

  6. git bash 使用自带 curl 命令出现乱码解决方法

    前言 使用过 git  的小伙伴应该都不会陌生,git 自带一个终端 git bash      类似于 window 自带的 dos git 官网下载:https://git-scm.com/dow ...

  7. BASH BUILTIN COMMANDS 内建命令

    除非另外说明,这一章介绍的内建命令如果接受 - 引导的选项,那么它也接受 -- 作为参数,来指示选项的结束 : [arguments] 没有效果:这个命令除了扩展 arguments 并且作任何指定的 ...

  8. Bash shell的内建命令:type

    type指令是用来观察指令时来自于外部指令还是内建在bash中的指令. type  [-tpa]  name 选项与参数: :不加任何选项与参数时,type会显示出name是外部指令还是bash内建指 ...

  9. bash的for循环从命令读取值

    bash的for循环可以很方便地从命令读取值,还可以指定分割值 下面的程序可以打印文件的内容,前面加上行号 #!/bin/bash # 打印每一行的内容,前面加行号 filename="/h ...

随机推荐

  1. ASP.NET SignalR

    何为ASP.NET SignalR,有什么用 ASP.NET SignalR是一个ASP.NET库,是为了实现实时web通信而创造的,能让服务器与客户端实现即时通信,而不需要服务器等待接收到客户端请求 ...

  2. [转载]彻底弄清struct和typedef struct

    struct和typedef struct 分三块来讲述: 1 首先://注意在C和C++里不同 在C中定义一个结构体类型要用typedef: typedef struct Student { int ...

  3. MVC 问答

    1.View含有什么,默认就念有Models吗? 不是,ViewBag是一个空对象.ViewBag 与 Models 不是必须一起使用的 . 2.Models 可用可不用?存在意义?

  4. C#.NET 大型通用信息化系统集成快速开发平台 4.1 版本 - 区域管理功能增强(电子商务方向)

    由于公司是面向全国服务的.全国各地都有分公司,需要管理到覆盖全国的各种业务,各种业务系统信息系统的数据都需要规范化. 公司开展网络订单功能,在全国范围内实现网络下单.提高工作效率,提高各公司之间的数据 ...

  5. 学习C++.Primer.Plus 10 对象和类

    1.类的声明和定义 类的声明和定义. 类声明的格式如下: class className { private://private 是类对象的默认访问控制,因此,可以省略 data member del ...

  6. 关于javascript中apply()和call()方法的区别

    如果没接触过动态语言,以编译型语言的思维方式去理解javaScript将会有种神奇而怪异的感觉,因为意识上往往不可能的事偏偏就发生了,甚至觉得不可理喻.如果在学JavaScript这自由而变幻无穷的语 ...

  7. Css-深入学习之弧形切角矩形

    本文是作者从别的网站和文章学习了解的知识,简单做了个笔记,想要学习更多的可以参考这里:[css进阶]伪元素的妙用--单标签之美,奇思妙想 (弧形切角矩形) 代码: width: 180px; heig ...

  8. .pop ----remove 删除

    s = {1,2,3,4,5,6,'sn','7'} s.pop()#删除随机值 print(s)#{2, 3, 4, 5, 6, '7', 'sn'} s.remove('sn')#删除值 prin ...

  9. js版弹力球实例

    <!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>弹 ...

  10. js点击某个图标或按钮弹出文件选择框

    <HTML> <head> <script type="text/javascript" src="script/jquery-1.6.2. ...