在 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. CSS控制TD内的文本超出指定宽度后不换行而用省略号代替

    <style type="text/css"> .lineOverflow { width: 100px; border:#000 solid 1px; text-ov ...

  2. php使用curl 检测socks5 代理的可用性

    少废话  直接粘代码 <?php    define('PROXY_CONF', 'ip:port');    define('PROXY_CHECK_URL', 'http://www.bai ...

  3. 虚基类&虚继承

    发现这个月准备竞赛完全没有更新哎... 改了下某华大一c++测试题...网上对虚继承讲的要么太繁琐要么不到位,自力更生 #include<iostream> #include<fst ...

  4. hdu 5652 India and China Origins 并查集

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5652 题目大意:n*m的矩阵上,0为平原,1为山.q个询问,第i个询问给定坐标xi,yi,表示i年后这 ...

  5. 基于ionic+cordova+angularJs从零开始搭建自己的移动端H5 APP

    这里详细介绍下如何用ionic+cordova+angularjs搭建自己的移动端app,包括环境搭建,框架使用等,具体项目已放置在github上,可下载下来自行启动. 下载地址:https://gi ...

  6. Linux下oracle环境变量无效问题

    今天在维护oracle数据库时,查看监听的状态,执行 #lsnrctl status 报错: -bash:lsnrctl:command not found.以前并不会这样,仔细想了一下,问题找到了, ...

  7. Android 从网络中获取数据时 产生部分数据乱码的解决

    产生部分数据乱码的解决 标签: android部分中文乱码 2014-04-12 23:24 12366人阅读 评论(10) 收藏 举报 分类: [Android 基础](15) 版权声明:本文为博主 ...

  8. fMRI: spatial smoothing

    Source: Brain voyager support Theoretical Background Spatial smoothing means that data points are av ...

  9. C#.NET 大型企业信息化系统集成快速开发平台 4.2 版本 - 访问频率限制功能实现、防止黑客扫描、防止恶意刷屏

    很多软件组件,大家都能想到了,大家也能做出来,但是成熟稳定.可靠.易用.功能全面,可信任,可相信,可开源就不是很容易,需要树立良好的口碑才可以. 1:往往会有黑客,进行撞库挖掘漏洞,很多系统的账户有可 ...

  10. matlab常用的字符串操作函数之一

    1,strcat和strvcat strcat:依次横向连接字符串: strvcat:依次纵向连接字符串: 实例1: >>a1='sophia '; >>a2='is a '; ...