Bash

計算目錄中有多少文件的最資源有效的方法是什麼?

  • January 16, 2020

CentOS 5.9

前幾天我遇到了一個目錄有很多文件的問題。為了計算它,我跑了ls -l /foo/foo2/ | wc -l

事實證明,單個目錄中有超過 100 萬個文件(長篇大論——根本原因正在修復)。

我的問題是:有沒有更快的計數方法?獲得計數的最有效方法是什麼?

簡短的回答:

\ls -afq | wc -l

(這包括.and ..,所以減去 2。)


當您列出目錄中的文件時,可能會發生三種常見情況:

  1. 列舉目錄中的文件名。這是不可避免的:如果不列舉目錄中的文件,就無法計算它們。
  2. 對文件名進行排序。Shell 萬用字元和ls命令執行此操作。
  3. 呼叫stat以檢索有關每個目錄條目的元數據,例如它是否是目錄。

#3 是迄今為止最昂貴的,因為它需要為每個文件載入一個 inode。相比之下,#1 所需的所有文件名都緊湊地儲存在幾個塊中。#2 浪費了一些 CPU 時間,但通常不會破壞交易。

如果文件名中沒有換行符,則簡單ls -A | wc -l告訴您目錄中有多少文件。請注意,如果您有 的別名ls,這可能會觸發呼叫stat(例如ls --colorls -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 之外,所有這些方法都對文件名進行排序。

引用自:https://unix.stackexchange.com/questions/90106