上文中曾講到,我在個人 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 上不須要這個,蘋果移植的時候考慮到本身的文件系統不區分大小寫,故意作了改良。