web-dev-qa-db-ja.com

なぜ `ls -l`は私よりも多くのファイルをカウントするのですか?

どうやら私は数えられない。 /mediaには3つのファイルがあると思います

$ tree /media
/media
├── foo
├── onex
└── zanna
3 directories, 0 files

ただし、ls -lは12を検出します。

$ ls -l /media
total 12
drwxr-xr-x  2 root root 4096 Jul 31 20:57 foo
drwxrwxr-x  2 root root 4096 Jun 26 06:36 onex
drwxr-x---+ 2 root root 4096 Aug  7 21:17 zanna

そして、ls -laを実行すると、上記に加えて...のみが取得されますが、カウントはtotal 20です

説明は何ですか?

25
Zanna

表示される12はファイルの数ではなく、消費されたディスクブロックの数です。

info coreutils 'ls invocation'から:

 For each directory that is listed, preface the files with a line
 `total BLOCKS', where BLOCKS is the total disk allocation for all
 files in that directory.  The block size currently defaults to 1024
 bytes, but this can be overridden (*note Block size::).  The
 BLOCKS computed counts each hard link separately; this is arguably
 a deficiency.

2つの追加ディレクトリ1220をカウントしているため、ls -laの代わりにls -lを使用すると、合計は.から..になります。各(空の)ディレクトリに4つのディスクブロックを使用しているため、合計は3×4から5×4になります(おそらく、各ディレクトリにoneディスクブロック4096バイトを使用しています) ; infoページが示すように、ユーティリティはディスク形式をチェックしませんが、特に指示がない限り、1024のブロックサイズを想定します。

単にファイルの数を取得したい場合は、次のようなものを試してみてください

ls | wc -l
33
user4556274

ser4556274は既に回答済み thewhy私の答えは、howの追加情報を提供して、ファイルを適切にカウントすることだけです。

Unixコミュニティでは、一般的なコンセンサスは、 lsの出力の解析は非常に悪い考えです です。ファイル名には制御文字または隠し文字が含まれる可能性があるためです。たとえば、ファイル名に改行文字があるため、ls | wc -lls(含まれている)の出力に5行あることが示されていますが、実際にはディレクトリに4つのファイルしかない。

$> touch  FILE$'\n'NAME                                                       
$> ls                                                                         
file1.txt  file2.txt  file3.txt  FILE?NAME
$> ls | wc -l
5

方法#1:ユーティリティの検索

findコマンドは、通常ファイル名の解析に使用されますが、ここでは inode番号 を出力することで役立ちます。ディレクトリでもファイルでも、一意のiノード番号は1つだけです。したがって、-printf "%i\n"を使用し、.を介して-not -name "."を除外すると、ファイルの正確なカウントを取得できます。 (-maxdepth 1を使用してサブディレクトリへの再帰的な下降を防ぐことに注意してください)

$> find  -maxdepth 1 -not -name "." -print                                    
./file2.txt
./file1.txt
./FILE?NAME
./file3.txt
$> find  -maxdepth 1 -not -name "." -printf "%i\n" | wc -l                    
4

方法#2:globstar

シンプル、迅速、そしてほとんどポータブルな方法:

$ set -- * 
$ echo $#
228

setコマンドは、シェルの位置パラメーター($<INTEGER>のようにecho $1変数)を設定するために使用されます。これは、配列不足の/bin/sh制限を回避するためによく使用されます。追加のチェックを実行するバージョンは、Unix&Linuxの Gille's answer overにあります。

bashなどの配列をサポートするシェルでは、次を使用できます。

items=( dir/* )
echo ${#items[@]}

コメント内のスチールドライバー によって提案されたとおり。

findおよびglobstarを使用したwcメソッドと同様のトリックをstatとともに使用して、行ごとにiノード番号をカウントできます。

$> LC_ALL=C stat ./* --printf "%i\n" | wc -l                                          
4

別の方法は、forループでワイルドカードを使用することです。 (このテストでは、別のディレクトリを使用して、このアプローチがサブディレクトリに下降するかどうかをテストします。サブディレクトリには下降しません。16は、~/binの検証済みアイテム数です)

$> count=0; for item in ~/bin/* ; do count=$(($count+1)) ; echo $count ; done | tail -n 1                                
16

方法#3:他の言語/通訳

Pythonは、os.listdir()関数(再帰的ではなく、引数として指定されたディレクトリ内の項目のみをリストします)を指定したリストの長さを出力することで、問題のあるファイル名にも対処できます。

$> python -c "import os ; print os.listdir('.')"                              
['file2.txt', 'file1.txt', 'FILE\nNAME', 'file3.txt']
$>  python -c "import os ; print(len(os.listdir('.')))"                    
4

こちらもご覧ください

18