計算目錄中有多少文件的最資源有效的方法是什麼?
CentOS 5.9
前幾天我遇到了一個目錄有很多文件的問題。為了計算它,我跑了
ls -l /foo/foo2/ | wc -l
事實證明,單個目錄中有超過 100 萬個文件(長篇大論——根本原因正在修復)。
我的問題是:有沒有更快的計數方法?獲得計數的最有效方法是什麼?
簡短的回答:
\ls -afq | wc -l
(這包括
.
and..
,所以減去 2。)當您列出目錄中的文件時,可能會發生三種常見情況:
- 列舉目錄中的文件名。這是不可避免的:如果不列舉目錄中的文件,就無法計算它們。
- 對文件名進行排序。Shell 萬用字元和
ls
命令執行此操作。- 呼叫
stat
以檢索有關每個目錄條目的元數據,例如它是否是目錄。#3 是迄今為止最昂貴的,因為它需要為每個文件載入一個 inode。相比之下,#1 所需的所有文件名都緊湊地儲存在幾個塊中。#2 浪費了一些 CPU 時間,但通常不會破壞交易。
如果文件名中沒有換行符,則簡單
ls -A | wc -l
告訴您目錄中有多少文件。請注意,如果您有 的別名ls
,這可能會觸發呼叫stat
(例如ls --color
或ls -F
需要知道文件類型,這需要呼叫stat
),因此請從命令行呼叫command ls -A | wc -l
或\ls -A | wc -l
避免使用別名。如果文件名中有換行符,是否列出換行符取決於 Unix 變體。GNU coreutils 和 BusyBox 預設顯示
?
換行符,所以它們是安全的。呼叫
ls -f
以列出條目而不對其進行排序(#2)。這會自動打開-a
(至少在現代系統上)。該-f
選項在 POSIX 中,但具有可選狀態;大多數實現都支持它,但不支持 BusyBox。該選項-q
將包括換行符在內的不可列印字元替換為?
; 它是 POSIX,但 BusyBox 不支持,因此如果您需要 BusyBox 支持,請忽略它,但會導致名稱中包含換行符的文件數量過多。如果目錄沒有子目錄,那麼大多數版本的
find
不會呼叫stat
它的條目(葉子目錄優化:連結數為 2 的目錄不能有子目錄,所以find
不需要查找條目的元數據,除非條件如-type
要求)。find . | wc -l
如果該目錄沒有子目錄並且沒有文件名包含換行符,那麼一種可移植的、快速的對目錄中的文件進行計數的方法也是如此。如果目錄沒有子目錄但文件名可能包含換行符,請嘗試其中一種(如果支持,第二種應該會更快,但可能不會很明顯)。
find -print0 | tr -dc \\0 | wc -c find -printf a | wc -c
另一方面,
find
如果目錄有子目錄,則不要使用:甚至find . -maxdepth 1
呼叫stat
每個條目(至少使用 GNU find 和 BusyBox find)。您避免排序(#2),但您付出了 inode 查找(#3)的代價,這會降低性能。在沒有外部工具的 shell 中,您可以使用
set -- *; echo $#
. 這會失去點文件(名稱以 開頭的文件.
)並在空目錄中報告 1 而不是 0。這是對小目錄中的文件進行計數的最快方法,因為它不需要啟動外部程序,但是(在 zsh 中除外)由於排序步驟(#2)而浪費了較大目錄的時間。
- 在 bash 中,這是一種統計目前目錄中文件的可靠方法:
shopt -s dotglob nullglob a=(*) echo ${#a[@]}
- 在 ksh93 中,這是一種統計目前目錄中文件的可靠方法:
FIGNORE='@(.|..)' a=(~(N)*) echo ${#a[@]}
- 在 zsh 中,這是一種統計目前目錄中文件的可靠方法:
a=(*(DNoN)) echo $#a
如果您
mark_dirs
設置了該選項,請確保將其關閉:a=(*(DNoN^M))
.
- 在任何 POSIX shell 中,這是計算目前目錄中文件的可靠方法:
total=0 set -- * if [ $# -ne 1 ] || [ -e "$1" ] || [ -L "$1" ]; then total=$((total+$#)); fi set -- .[!.]* if [ $# -ne 1 ] || [ -e "$1" ] || [ -L "$1" ]; then total=$((total+$#)); fi set -- ..?* if [ $# -ne 1 ] || [ -e "$1" ] || [ -L "$1" ]; then total=$((total+$#)); fi echo "$total"
除了 zsh 之外,所有這些方法都對文件名進行排序。