LinuxにO(1)(ファイルの数とは無関係に)で)ディレクトリ内のファイルの数を計算する方法はありますか?最初に?O(1)でない場合、かなり効率的な方法はありますか?
ls | wc -l
の代替品を探しています。
readdirはあなたが思うほど高価ではありません。このコツは、各ファイルのstatの実行と、(オプションで)lsの出力のソートを回避することです。
/bin/ls -1U | wc -l
シェルのエイリアスを回避し、出力をソートせず、1行に1ファイルをリストします(出力をwcにパイプする場合は厳密には必要ありません)。
元の質問は、「ディレクトリのデータ構造にはエントリ数のカウントが格納されていますか?」と言い換えることができますが、答えは「いいえ」です。 readdir(2)/ getdents(2)よりも効率的なファイルのカウント方法はありません。
与えられたディレクトリをstat '(stat(1)またはstat(2))して、そのディレクトリへのリンクの数を観察することにより、リスト全体を走査することなく、与えられたディレクトリのサブディレクトリの数を得ることができます。 N個の子ディレクトリを持つ特定のディレクトリには、N + 2のリンクカウントがあり、各サブディレクトリの「..」エントリへのリンクが1つ、「。」へのリンクが2つあります。および指定されたディレクトリの「..」エントリ。
ただし、リスト全体を走査しないと、すべてのファイルの数(通常のファイルでもサブディレクトリでも)を取得することはできません。これは正しいことです。
"/ bin/ls -1U"コマンドは、すべてのエントリを取得するわけではありません。 onlyドット(。)文字で始まらないディレクトリエントリを取得します。たとえば、多くのログイン$ HOMEディレクトリにある ".profile"ファイルはカウントされません。
「/ bin/ls -f」コマンドまたは「/ bin/ls -Ua」コマンドを使用して、ソートを回避し、すべてのエントリを取得できます。
おそらく残念なことに、「/ bin/ls -f」コマンドまたは「/ bin/ls -Ua」コマンドも「。」を数えます。および各ディレクトリにある「..」エントリ。次のように、これらの2つのエントリがカウントされないようにするには、カウントから2を引く必要があります。
expr `/bin/ls -f | wc -l` - 2 # Those are back ticks, not single quotes.
この場合の "wc"のように、 "ls"出力をパイプする場合、 "/ bin/ls -Ua"コマンドで--format = single-column(-1)オプションは必要ありません。 「ls」コマンドは、出力が端末でない場合、その出力を単一の列に自動的に書き込みます。
ls
の_-U
_オプションはPOSIXにはなく、OS Xのls
ではGNU ls
とは意味が異なります。つまり、_-t
_および_-l
_になります。変更時間ではなく作成時間を使用します。 _-f
_は、POSIXではXSI拡張機能です。 GNU ls
のマニュアルでは、_-f
_を_do not sort, enable -aU, disable -ls --color
_として、_-U
_を_do not sort; list entries in directory order
_として説明しています。
POSIXは_-f
_を次のように記述します:
各引数を強制的にディレクトリとして解釈し、各スロットで見つかった名前をリストします。このオプションは、_
-l
_、_-t
_、_-s
_、および_-r
_をオフにし、_-a
_をオンにします。順序は、エントリがディレクトリに表示される順序です。
_ls|wc -l
_のようなコマンドは、ファイル名に改行が含まれていると誤った結果を返します。
Zshでは、次のようなことができます。
_a=(*(DN));echo ${#a}
_
D
(_glob_dots
_)は、名前がピリオドで始まるファイルを含み、N
(_null_glob
_)は、コマンドが空のディレクトリでエラーにならないようにします。
またはbashで同じ:
_shopt -s dotglob nullglob;a=(*);echo ${#a[@]}
_
IFS
にASCII桁が含まれる場合は、_${#a[@]}
_を二重引用符で囲みます。 _shopt -u failglob
_を追加して、failglob
が設定されていないことを確認します。
移植可能なオプションは、find
を使用することです。
_find . ! -name . -Prune|grep -c /
_
ファイル名に改行が含まれていない場合、_grep -c /
_を_wc -l
_に置き換えることができます。 _! -name . -Prune
_は、_-mindepth 1 -maxdepth 1
_のポータブルな代替品です。
または、名前がピリオドで始まるファイルを通常含まない別の方法もあります。
_set -- *;[ -e "$1" ]&&echo "$#"
_
ただし、上記のコマンドには、bashのdotglob
またはzshの_glob_dots
_などのオプションが設定されている場合、名前がピリオドで始まるファイルが含まれます。 _*
_がどのファイルとも一致しない場合、コマンドはデフォルト設定のzshでエラーになります。
私はこのコマンドを使用しました。チャームのように機能します。サブディレクトリであるmaxdepthを変更するためだけです。
find * -maxdepth 0 -type d -exec sh -c "echo -n {} ' ' ; ls -lR {} | wc -l" \;
find
を使用すると、これをより詳細に制御できると思います。
find <path> -maxdepth 1 -type f -printf "." | wc -c
find -maxdepth 1
は、ファイルの階層に深く入りません。-type f
はファイルのみにフィルタリングを許可します。同様に、-type d
ディレクトリ。-printf "."
はすべての一致に対してドットを出力します。wc -c
は文字をカウントするため、print
...によって作成されたドットをカウントします。これは、指定されたパスに存在するファイルの数をカウントすることを意味します。私の知る限り、これ以上の選択肢はありません。この情報はこの質問の主題から外れている可能性があり、Linux(一般にUnix)のディレクトリは他のファイルのリストを含む単なる特別なファイルであることをすでに知っているかもしれません(正確な詳細は特定のファイルに依存することを理解しています)システムですが、これは一般的な考え方です)。また、リスト全体を走査せずに、エントリの総数を見つける必要はありません。私が間違っている場合は訂正してください。
現在のディレクトリ内のすべてのファイルの数については、これを試してください:
ls -lR * | wc -l