为什么`ls -l` 计数的文件比我多?

Zan*_*nna 29 command-line files ls

显然我数不过来。我认为有三个文件/media

$ tree /media
/media
??? foo
??? onex
??? zanna
3 directories, 0 files
Run Code Online (Sandbox Code Playgroud)

然而,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
Run Code Online (Sandbox Code Playgroud)

而且,如果我这样做,ls -la我只会得到.并且..除了上述之外,但计数是total 20

解释是什么?

use*_*274 38

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.
Run Code Online (Sandbox Code Playgroud)

当您使用而不是因为您正在计算两个额外的目录时,总数从12到:和。您为每个(空)目录使用四个磁盘块,因此您的总数从 3 × 4 变为 5 × 4。(很可能,您为每个目录使用了一个4096 字节的磁盘块;如页面所示,实用程序不检查磁盘格式,但除非另有说明,否则假定块大小为。)20ls -lals -l...info1024

如果您只想获取文件数,您可以尝试类似

ls | wc -l
Run Code Online (Sandbox Code Playgroud)

  • “如果文件名中有一个新行”...... _shudder_ (20认同)
  • `ls | 如果文件名中有换行符, wc -l` 将失败。这更具弹性:`find 。-mindepth 1 -maxdepth 1 -printf 。| wc -c` (13认同)
  • 正如`man ls` 会告诉你的那样,你可以使用`-b`(转义它们)或`-q`(省略它们)来避免控制字符。所以为了计数,`ls -1q | wc -l` 可以安全准确地显示非隐藏文件。`ls -1qA | wc -l` 来计算隐藏文件(但不包括 `.` 和 `..`)。我使用 `-1` 而不是 `-l` 因为它应该更快。 (8认同)

Ser*_*nyy 18

user4556274 已经回答原因。我的回答仅用于提供有关如何正确计算文件的附加信息。

在 Unix 社区中,普遍的共识是解析 的输出ls是一个非常非常糟糕的主意,因为文件名可以包含控制字符或隐藏字符。例如,由于文件名中的换行符,我们ls | wc -l告诉我们ls(它确实有)的输出中有 5 行,但实际上目录中只有 4 个文件。

$> touch  FILE$'\n'NAME                                                       
$> ls                                                                         
file1.txt  file2.txt  file3.txt  FILE?NAME
$> ls | wc -l
5
Run Code Online (Sandbox Code Playgroud)

方法#1:查找实用程序

The find command, which is typically used for working around parsing filenames, can help us here by printing the inode number. Be it a directory or a file, it only has one unique inode number. Thus, using -printf "%i\n" and excluding . via -not -name "." we can have an accurate count of the files. (Note the use of -maxdepth 1 to prevent recursive descending into subdirectories)

$> 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
Run Code Online (Sandbox Code Playgroud)

Method #2 : globstar

Simple, quick, and mostly portable way:

$ set -- * 
$ echo $#
228
Run Code Online (Sandbox Code Playgroud)

set command is used to set positional parameters of the shell ( the $<INTEGER> variables, as in echo $1 ). This is often used to work around /bin/sh limitation of lacking arrays. A version that performs extra checks can be found in Gille's answer over on Unix&Linux.

In shells that support arrays, such as bash, we can use

items=( dir/* )
echo ${#items[@]}
Run Code Online (Sandbox Code Playgroud)

as proposed by steeldriver in the comments.

Similar trick to find method which used wc and globstar can be used with stat to count inode numbers per line:

$> LC_ALL=C stat ./* --printf "%i\n" | wc -l                                          
4
Run Code Online (Sandbox Code Playgroud)

An alternative approach is to use a wildcard in for loop. (Note, this test uses a different directory to test whether this approach descends into subdirectories, which it does not - 16 is the verified number of items in my ~/bin )

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

Method #3: other languages/interpreters

Python can also deal with problematic filenames via printing the length of a list given my os.listdir() function (which is non-recursive, and will only list items in the directory given as argument).

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

See also

  • 在 bash 中,另一种选择是使用数组,例如 `items=( dir/* ); echo ${#items[@]}`(添加`shopt -s dotglob` 以包含隐藏文件)。 (2认同)
  • 为什么不直接使用`print(len(os.listdir('.')))`?要键入的字符更少,并且还避免访问双下划线属性。 (2认同)