2010年1月18日 星期一

/bin/rm: argument list too long 的解法和原因

若不小心在一個目錄下產生太多暫存檔, 直接 rm -f *.tmp 會出現這個錯誤訊息:
/bin/rm: argument list too long
若不幸的同一目錄下還有其它檔案, 不能一次砍掉整個目錄,這時可以透過 find 和 xargs 解掉這問題:
find . -name "*.tmp" -print0 | xargs -0 rm
這樣就會透過 xargs 把檔案名稱傳給 rm。注意,find、xargs 分別加上 -print0 和 -0 是為了避開檔名中含有空白字元, 詳情請 man find 並閱讀 -print0 的部份。

這篇這篇解釋了為什麼有這個問題, 以及為何 find + xargs 可以避開它。問題的源頭不是 bash 也不是 rm, 而是 kernel 執行命令時會先將參數存到 buffer 裡。buffer 的大小是 MAX_ARG_PAGES * "page 大小" --- 一般而言是 32 * 4k = 128k。

那為啥 xargs沒這問題呢? 對照 man page 的說法, 以及粗略地 trace過 xargs 的原始碼後, 發覺 xargs 自己有管一個參數 buffer, 一但 buffer 滿了, xargs 就會執行傳給它的指令和起始參數, 接著清空 buffer, 再繼續讀剩下的參數。換句話說, xargs 會分多次執行完所有參數。假設要刪除的檔案是 1, 2, ..., 100000000。xargs 有可能這麼做:
rm 1 2 ... 1000000
rm 1000001 1000002 ... 2000000
...
我沒有算 buffer size 和檔名順序, 上面只是示意用的例子。


Update 

Victor 說 find 加上參數  -delete 就可以了, 即:
find . -name "*.tmp" -delete
那........ 以上的說明, 就當作 find + xargs 的靈活應用之一吧。

沒有留言:

張貼留言

在 Fedora 下裝 id-utils

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