上文中曾講到,我在我的 Mac 上發現很多和 Bash 內部命令同名的外部命令,在那 24 個外部命令中,我發現個奇怪的現象:它們中有 15 個居然是 Shell 腳本,更奇怪的是,居然是同一個 Shell 腳本的硬鏈接:
$ find /usr/bin -inum 376183 /usr/bin/alias /usr/bin/bg /usr/bin/cd /usr/bin/command /usr/bin/fc /usr/bin/fg /usr/bin/getopts /usr/bin/hash /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 上不需要這個,蘋果移植的時候考慮到自己的文件系統不區分大小寫,故意做了改良。
文章列表