上文中曾讲到,我在个人 Mac 上发现不少和 Bash 内部命令同名的外部命令,在那 24 个外部命令中,我发现个奇怪的现象:它们中有 15 个竟然是 Shell 脚本,更奇怪的是,竟然是同一个 Shell 脚本的硬连接:html
$ find /usr/bin -inum 376183bash /usr/bin/aliasdom /usr/bin/bgui /usr/bin/cd操作系统 /usr/bin/commandhtm /usr/bin/fcblog /usr/bin/fg进程 /usr/bin/getoptsci /usr/bin/hashget /usr/bin/jobs /usr/bin/read /usr/bin/type /usr/bin/ulimit /usr/bin/umask /usr/bin/unalias /usr/bin/wait |
看看脚本的内容:
$ cat /usr/bin/cd #!/bin/sh # $FreeBSD: src/usr.bin/alias/generic.sh,v 1.2 2005/10/24 22:32:19 cperciva Exp $ # This file is in the public domain. builtin `echo ${0##*/} | tr \[:upper:] \[:lower:]` ${1+"$@"} |
脚本只有一行,它的做用是什么?我分析了一下,当用户正常输入一个内部命令好比说 cd 时,Shell 确定会把它当成内部命令执行,只有用户不当心把 cd 输入成 CD,因为 Mac 的文件系统不区分大小写,Shell 才会去执行这个外部的脚本。这个脚本拿到 $0 的值,也就是 /usr/bin/CD,砍掉路径,而后把大小字母替换成小写,也就是 cd,而后去执行 cd,同时带上参数。但我有几点想不通,这里的 builtin 彻底是多余的,${1+"$@"} 也彻底能够简写成 "$@",最重要的是,执行这些脚本是几乎没有任何意义的,由于 Shell 脚本是在当前 Shell 进程的新起的 Shell 进程里执行的,也就是说执行 CD / 至关于执行 bash -c 'cd /',当前 Shell 的工做目录其实并无改变,除了 cd,其余命令也同样,虽然执行了,但彻底没用,我再用 alias 和 unalias 演示一下:
$ alias 'll=ls -l' $ Alias $ Unalias ll /usr/bin/Unalias: line 4: unalias: ll: not found $ alias alias ll='ls -l' |
这么作的出发点是什么,输入 CD 应该报错才对啊,不报错反而执行了没效果,多让人困惑的行为。
因而我在网上查了一下,发现一篇日语的文章详详细细的介绍了这个脚本的前因后果。原来这个脚本存在的缘由是:POSIX 标准要求操做系统要提供这 14 个内部命令对应的外部命令,以便 env、find、nice、nohup、time、xargs 这几个外部命令调用,好比 env cd。POSIX 又为何这么规定,那就不知道了,但的确没卵用啊,怪不得 Linux 就没有遵照这个规范。
最初这个脚本诞生于 FreeBSD 上,为何加上 builtin 和用 ${1+"$@"} 都是由于 FreeBSD 操做系统的缘由,那篇文章有讲。还有最初的脚本是没有 tr \[:upper:] \[:lower:] 这一部分的,FreeBSD 上不须要这个,苹果移植的时候考虑到本身的文件系统不区分大小写,故意作了改良。