在 Ubuntu 下執行指令後, 若沒有安裝指令的話, 會出現提示:
$ apt-rdepends The program 'apt-rdepends' is currently not installed. You can install it by typing: sudo apt-get install apt-rdepends
但若直接用 bash 執行, 卻不會有這效果:
$ bash -c apt-rdepends bash: apt-rdepends: command not found
以前覺得很好奇, Ubuntu 怎麼做到這件事的, 知道 strace 以後, 追這類原因簡單許多, 只要有輸入和輸出訊息, 就可夾擊出一些線索。
- 在 terminal 1 輸入 echo $$ 取得該 bash 的 PID
- 在 terminal 2 輸入 sudo strace -obash.trace -f -s512 -p PID
- -obash.trace 表示將輸出存到 bash.trace, 訊息很多, 通常都會寫到檔案裡
- -s512 表示輸出訊息最多到 512, 預設行寬有點短, 之後不方便找輸出的訊息
- -f 表示一起追蹤 child process, 這點很重要, 沒加 -f 就追不到 bash 使用的其它子程序, 而關鍵就在 bash 叫起的子程序
- -p PID 表示追踪其它 process, 照理說同一個使用者不用 root 權限應該也能看, 不知為啥不通
- 在 terminal 1 輸入 apt-rdepends, 因為 strace 有用 "-f", 速度會慢很多。等待指令完成
- 在 terminal 2 按 Ctrl+C 中斷 strace, 觀察 bash.trace
搜一下 "apt-rdepends" 會看到 bash 在嘗試各種 path 後都找不到檔案:
16520 stat("/home/fcamel/bin/apt-rdepends", 0x...) = -1 ENOENT... 16520 stat("/usr/local/sbin/apt-rdepends", 0x...) = -1 ENOENT... 16520 stat("/usr/local/bin/apt-rdepends", 0x...) = -1 ENOENT... 16520 stat("/usr/sbin/apt-rdepends", 0x...) = -1 ENOENT... 16520 stat("/usr/bin/apt-rdepends", 0x...) = -1 ENOENT... 16520 stat("/sbin/apt-rdepends", 0x...) = -1 ENOENT... 16520 stat("/bin/apt-rdepends", 0x...) = -1 ENOENT... 16520 stat("/usr/games/apt-rdepends", 0x...) = -1 ENOENT... 16520 stat("/home/fcamel/bin/apt-rdepends", 0x...) = -1 ENOENT... 16520 stat("/home/fcamel/bin/apt-rdepends", 0x...) = -1 ENOENT...
之後在別的 child process 呼叫外部程式執行 /usr/lib/command-not-found:
16877 execve("/usr/bin/python", ["/usr/bin/python", "/usr/lib/command-not-found", "--", "apt-rdepends"], [/* 38 vars */]) = 0
若想研究怎麼找出該裝的套件, 可以研究 "/usr/lib/command-not-found"。若想研究 bash 如何判斷在有 terminal 的情況下要多找安裝的指令, 可以自己編含 debug symbol 的 bash, 再用 gdb 找出相關位置, 再讀附近的原始碼。這樣一來, 至少知道需要追的時候該如何進行, 剩下就是增加經驗提昇追程式的速度了。
2012-01-23 更新
經 wens 提醒, 原來是用 bash 的 hook 做的, 見 /etc/bash.bashrc 了解設定, man bash 在 COMMAND EXECUTION 那節有說明。
這個主題,其實對shell有基本的了解就知道了。
回覆刪除shell會根據PATH環境變數去找你要的指令,找不到就說command not found.
至於他怎麼搜,你可以看看execlp這個函式.
(不過bash不見得是呼叫這個,他也有可能自己實作)
後面提供你該裝哪個套件的,是在/etc/bash.bashrc系統初始檔裡面呼叫到的
(我個人不是很喜歡這東西... 他會讓反應變慢)
謝啦, 我知道 PATH, 沒想到有 command_not_found_handle, 之前找 auto completion 最後也發現是用 bash 提供的 hook 實作的, 看來要先查 man bash 才是。
回覆刪除