Replying to f6XF

《从 zsh 回到 bash》

不知不觉用了几年的 zsh,起初是因为别人都说 zsh 更好,就试了一下。没想到后来 macOS 升级,把系统默认的 shell 换成了 zsh,就以为 zsh 真的很好。但这几天写一些方便工作用的 shell 脚本(其实也就是几行的那种函数),发现 zsh 的语言并不比 bash 的好,有些地方也许更差一些。

shell 语言本来就是垃圾,但是 zsh 却又跟 bash 的语言有所不同。如果要用 zsh 写脚本,就得再理解一些变种垃圾语法。好不容易在网上找到如何用 bash 的离奇语法表达某个本来很简单的事情,结果用 zsh 就不灵,得换一种离奇的语法才行。

有些其它语言里很简单的事情,比如如何把从某命令行输出拿到的字符串分切成一个数组(split),我至今没有搞明白 zsh 要怎么写,网上搜出来的“bash 答案”全都不灵。如何表达一个数组的长度?bash 必须用很奇葩的语法,而 zsh 又不一样。等你好不容易搞明白,如果遇到某个 server 没有 zsh,只有 bash,又不让你自己装东西那种,那就得再改回去。

于是我忽然想起这件事来:zsh 到底有什么好?回想了一下,zsh 也就是提供了一些五颜六色花哨的 theme 而已。一旦装上 oh my zsh 那东西,整个 .zshrc 文件就给变了样,多了一大堆东西,太多的注释。命令行补全,shell history,都变成了某些其他人认为“方便”的形式,而其实很混乱。

每个 theme 都有太多花哨,好不容易找到个简单点的 theme(af-magic),可是每次拷贝 terminal 的内容做笔记,都得把背景上那个 user@host 的水印给一起 copy 下来。另外一些 theme,总喜欢放一些奇怪的花样字符在提示符里,拷贝粘贴到其他地方就成了乱码。这叫做方便?感觉 zsh 是拿来看的,不是拿来用的。

之前我还试过一种新鲜的 shell 叫 fish,开头也是看起来很“先进”和“高效率”的样子,等你需要写点自动化的脚本才发现它的语言有多垃圾,比 bash 还要差很多。zsh 的语言没有 fish 那么差,基本和 bash 差不多,但总有那么一点点差别,让你又浪费很多时间在上面。总之,不能指望任何 unix shell 的语言是好语言。

后来忽然有一天,我问自己,我为什么要用 fish?于是忽然就换回了 bash。结果过了一段时间,有人给我推荐 zsh,我觉得这个语言应该和 bash 一样的,可能会好一点,于是又上了贼船…… 如果你知道一些 Unix 的历史,就知道以前还出现过一些新鲜的 shell,什么 csh,ksh,tcsh,…… 现在都成了昨日黄花 😄

总是有人前仆后继,要设计新的 shell,却又不明白好的 shell 语言应该是什么样的。每一次觉得新鲜的尝试,最后都是一样的结局。所以我现在又再次轮回,想换回 bash,结果发现 macOS 自带的 bash,启动居然会提示你 default shell 已经是 zsh 了,还提示你如何切换到 zsh(如图)。

Last login: Sun May 21 16:54:37 on ttys003

The default interactive shell is now zsh.

To update your account to use zsh, please run `chsh -s /bin/zsh`.

For more details, please visit https ://support.apple.com/kb/HT208050.

~ $

我可不想每次打开 terminal 都看到这样的提示。找了一会想把它去掉,却没找到这提示是在什么启动脚本里。结果搜索了一下网络,发现必须要在 .bash_profile 里加上这样一行,才能去掉这个提示:

export BASH_SILENCE_DEPRECATION_WARNING=1

你可以用 grep 在 macOS 自带的 /bin/bash 二进制文件里找到这个字符串:please run `chsh -s /bin/zsh`。

~ $ grep ‘please run `chsh -s /bin/zsh`’ /bin/bash

Binary file /bin/bash matches

所以这提示不是在某个启动脚本里,而是改在了 macOS 自带的 bash 的代码里。改了 bash 的二进制代码来强推 zsh,我觉得这个做法也太离谱,让人感觉很不正常。一般做法最多也就在默认启动脚本里暂时放个提示,用户可以随时删掉,不至于连二进制代码都改了,为了提示你用 zsh。

网络上能找到的其它去掉这个提示的办法,就只有 brew install bash,而不用系统自带的 bash。如果你不幸升级到了最新的 macOS,那对不起,brew 告诉你这个 os 版本太新,还没有 bash。如果要把系统默认 shell 改成brew 的 bash,用 chsh -s /usr/local/bin/bash 居然还不行,说这个是 non-standard shell,得想其它办法。

~ » chsh -s /usr/local/bin/bash 1 ↵ yinwang@tofu

Changing shell for yinwang.

Password for yinwang:

chsh: /usr/local/bin/bash: non-standard shell

(注意右边拷贝下来的花哨“水印”)

反正就是感觉设置了重重障碍,不想让大家用 bash。可是越是这样强推 zsh,我就越是怀疑这里面有什么猫腻,越是不想用 zsh。

把系统 shell 改回 bash 之后,发现什么 shell completion,bash 也一样有。要在 prompt 显示 git 的 branch?bash 用几行就能改成方便的形式,也就改个 PS1 变量而已。

parse_git_branch() {

git branch 2> /dev/null | sed -e ‘/^[^*]/d’ -e ‘s/* \(.*\)/ (\1)/’

}

export PS1=“\[\033[32m\]\w\[\033[33m\]\$(parse_git_branch)\[\033[00m\] $ ”

所以我发现,之前觉得 zsh 提供的方便东西,本来在 bash 里就都不缺,我有什么理由要用 zsh 呢?我没有理由,所以我就一直用 bash。shell 语言是很差,但我只需要折腾会这一种。对于 *nix 系统,我再也不会考虑使用其它 shell。

《zsh 的主要功能分析》

换掉 zsh 回到 bash 之前,为了确保我没有漏掉什么好东西,我又搜索了一下“zsh better than bash”。发现除了“命令行补全”,更换“主题”之类 bash 本来就可以有的功能,zsh 恐怕就只剩下“直接输入目录名就能 cd”这样的了。也就是说你不用打 cd /etc,而是直接打 /etc,就能到 /etc 目录。

谁在乎多打少打 cd 两个字?这也叫超越 bash 的优势?然而这个功能被很多推 zsh 的文章(比如 http://t.cn/A6Nss9pv)排在 zsh 的“太多 feature”的首位。可笑不?

So Why Use It?

ZSH has too many features to list here, some just minor improvements to Bash, but here are some of the major ones:

* Automatic cd: Just type the name of the directory

* Recursive path expansion: For example “/u/lo/b” expands to “/usr/local/bin”

* Spelling correction and approximate completion: If you make a minor mistake typing a directory name, ZSH will fix it for you

* Plugin and theme support: ZSH includes many different plugin frameworks

Plugin and theme support is probably the coolest feature of ZSH and is what we’ll focus on here.

如果 Automatic cd 能被列在“主要 feature”的首位,这说明 zsh 还能有什么重要的 feature?我发现这段话堪称经典,值得仔细分析回味一下。几年前我是怎么落入这个傻的不行的圈套的?😄

很多人可能没有发现,Automatic cd 不但没有节省什么时间,而且并不是什么好东西。cd xyz 明确的说明了用户的意图是“切换目录到 xyz”。如果没有了 cd,直接输入 xyz,就和“执行 xyz 这个命令”的语义混淆了。如果能出现这样的混淆,就需要用户输入的时候更加小心,不然本来想输入命令,却被误以为要切换目录。混淆之后,再花时间来分析和改正,需要浪费用户更多的时间和脑力。而且这种事情会反复发生,为此浪费的时间,因此产生的烦躁累积起来,就很多了。这种混淆是完全没必要的,如果输入 cd 就不会发生这种混淆。所以明确输入 cd 两个字是更好的做法,简单明了,没有歧义。

再看看第二个主要 feature(Recursive path expansion):说 zsh 能自动把 “/u/lo/b” 这样的输入扩展成 “/usr/local/bin”?谁用过这功能?反正我不知道它有这功能,知道了我也不会用它。这样的自动扩展让人以为“方便”,而其实很容易混淆,浪费更多时间。要是 /u/lo/b 对应着多个可能的扩展怎么办?而且这种歧义是指数增长的。如果每级目录的名字有 2 种可能性,3 级目录就有 8 种可能性。出现了歧义,用户就得去选择,选择就需要花时间和脑力。而且要是前面或者中间的目录前缀你打错了字,就会发现怎么展不开,或者展开是错的,然后又要回去改,经历更多麻烦。所以这些目录名,我宁愿一个个的按 tab 展开,确认前一个扩展对了,再开始输入下一个,而不是一次性输入 /u/lo/b 这样的,然后按 tab 一下子展开。

第三个 feature 就更容易造成混淆:Spelling correction and approximate completion。按 tab 之后,它会试图把你打错的文件名或命令自动更正。但我第一次察觉到这个功能,就发现非常容易出错。我刚打了两个字,本来想 tab 补全一个命令,结果因为其中一个字打错了,所以它给我补全成了完全不同的另一个命令。我很惊讶,我没想到命令行怎么会变成那个样子,我从来没听说过那个命令。仔细看了一会才发现,原来是因为这两个字母出现在另一个命令名字的中间,所以 zsh 就给我“自动纠错”了。我宁愿它当时就什么都不要做,什么都别自动更正,这样我立即就会发现其中一个字母打错了,马上改掉,而不是看到自动更正的命令,然后才去想我刚才做了什么,展开成这样奇怪的结果,我在做什么……?

第四个 feature(Plugin and theme support)就不多说了。不但是多余的,而且这些花哨的配置 bash 也可以有。

另外,这段话开头说“some just minor improvements to Bash”,既然以上的鸡毛蒜皮事情能算 “major improvement”,那什么才算是“minor improvements”呢?我感觉就是那些微妙的 shell 语言改动了。然而恰恰是那些 minor 的语言改动,让你无法表达想要的东西,让网上搜到的 bash 的现成答案都不能拿来用。就这样,完美地制造了一些无解的问题。

很多人换成 zsh,可能都是因为看了这样的文章,列出了这“多种 feature”,而没有思考过,这些feature 真的重要,真的有用吗?自己其实没发现有什么好,就开始给别人推荐,结果大家都被这种洗脑宣传传染了。zsh 如此莫名其妙的大规模传播,人人都说它更好,却不知道好在哪里了,甚至成为了 macOS 的 default shell,我简直觉得这是 Matrix 在搞鬼了。😄

Reply to this note

Please Login to reply.

Discussion

No replies yet.