2012年1月23日 星期一

用 strace 找出 Ubuntu 如何提示未安裝的指令

在 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 那節有說明。

2 則留言:

  1. 這個主題,其實對shell有基本的了解就知道了。
    shell會根據PATH環境變數去找你要的指令,找不到就說command not found.

    至於他怎麼搜,你可以看看execlp這個函式.
    (不過bash不見得是呼叫這個,他也有可能自己實作)

    後面提供你該裝哪個套件的,是在/etc/bash.bashrc系統初始檔裡面呼叫到的
    (我個人不是很喜歡這東西... 他會讓反應變慢)

    回覆刪除
  2. 謝啦, 我知道 PATH, 沒想到有 command_not_found_handle, 之前找 auto completion 最後也發現是用 bash 提供的 hook 實作的, 看來要先查 man bash 才是。

    回覆刪除

在 Fedora 下裝 id-utils

Fedora 似乎因為執行檔撞名,而沒有提供 id-utils 的套件 ,但這是使用 gj 的必要套件,只好自己編。從官網抓好 tarball ,解開來編譯 (./configure && make)就是了。 但編譯後會遇到錯誤: ./stdio.h:10...