ls命令不适用于包含大量文件的目录


70

我的目录有大约500万个文件。当我尝试ls从此目录中运行命令时,系统消耗了大量内存,并且一段时间后挂起。除了使用ls命令以外,是否有一种有效的方法来列出文件?


11
确保您没有用于ls该用途的别名,--color否则-F将对lstat(2)每个文件进行别名。
斯特凡Chazelas

4
顺便说一句,将数百万个文件存储在单个目录中是一个很糟糕的主意。如果您控制目录布局,也许可以按某些标准将其拆分?
d33tah 2014年

那是纯粹的ls电话还是您使用期权?
Hauke Laging

1
@ d33tah是的,500万很多!我的根文件系统的限制为700万个inode。
Mikel 2014年

7
要输出500万个项目-您如何看待-简单的清单太多了-那么您想要该清单的用途是什么?
user151019 2014年

Answers:


66

避免使用以下内容进行排序:

ls --sort=none # "do not sort; list entries in directory order"

或者,等效地:

ls -U

10
我想知道列布局还会增加多少开销。添加-1标志可能会有所帮助。
Mikel 2014年

可能不多,但一点点帮助,对吗?:)
Mikel 2014年

1
@Mikel只是一个猜测,还是您已经测量过?在我看来,这-1需要更长的时间。
Hauke Laging

10
“ -1”很有帮助。“ ls -f -1”将避免任何统计调用,并立即打印所有内容。列输出(这是发送到终端时的默认值)使它首先缓冲所有内容。在我的系统上,在具有800万个文件的目录中使用btrfs(由“ seq 1 8000000 | xargs touch”创建),“ time ls -f -1 | wc -l”花费不到5秒,而“ time ls -f” -C | wc -l”需要30秒以上。
Scott Lamb

1
@ToolmakerSteve默认行为(-C当stdout是终端时,-1当它是管道时)令人困惑。在进行测试和测量时,您可以在查看输出(以确保命令按预期执行)和抑制输出(以避免终端应用程序吞吐量的混淆因素)之间切换。最好使用的是,在这两种模式中以相同的方式表现的命令,所以通过明确定义的输出格式-1-C-l
斯科特兰姆

47

ls实际上对文件进行排序并尝试列出它们,如果我们试图在目录中列出超过一百万个文件,这将成为巨大的开销。如链接所述,我们可以使用stracefind列出文件。但是,由于我有500万个文件,这些选项对于我的问题似乎也不可行。谷歌搜索一些位后,我发现,如果我们使用列出目录getdents(),它应该是更快,因为lsfindPython库使用readdir()它是速度较慢,但采用getdents()下面。

我们可以getdents()这里找到C代码来列出文件:

/*
 * List directories using getdents() because ls, find and Python libraries
 * use readdir() which is slower (but uses getdents() underneath.
 *
 * Compile with 
 * ]$ gcc  getdents.c -o getdents
 */
#define _GNU_SOURCE
#include <dirent.h>     /* Defines DT_* constants */
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/syscall.h>

#define handle_error(msg) \
       do { perror(msg); exit(EXIT_FAILURE); } while (0)

struct linux_dirent {
   long           d_ino;
   off_t          d_off;
   unsigned short d_reclen;
   char           d_name[];
};

#define BUF_SIZE 1024*1024*5

int
main(int argc, char *argv[])
{
   int fd, nread;
   char buf[BUF_SIZE];
   struct linux_dirent *d;
   int bpos;
   char d_type;

   fd = open(argc > 1 ? argv[1] : ".", O_RDONLY | O_DIRECTORY);
   if (fd == -1)
       handle_error("open");

   for ( ; ; ) {
       nread = syscall(SYS_getdents, fd, buf, BUF_SIZE);
       if (nread == -1)
           handle_error("getdents");

       if (nread == 0)
           break;

       for (bpos = 0; bpos < nread;) {
           d = (struct linux_dirent *) (buf + bpos);
           d_type = *(buf + bpos + d->d_reclen - 1);
           if( d->d_ino != 0 && d_type == DT_REG ) {
              printf("%s\n", (char *)d->d_name );
           }
           bpos += d->d_reclen;
       }
   }

   exit(EXIT_SUCCESS);
}

将上面的C程序复制到需要列出文件的目录中。然后执行以下命令。

gcc  getdents.c -o getdents
./getdents

时间示例getdents可能比快得多ls -f,具体取决于系统配置。以下是一些时间,表明在计算群集中通过NFS挂载列出包含约500k文件的目录时,速度提高了40倍。每个命令立即连续运行10次getdents,然后再运行ls -f。第一次运行的速度明显慢于所有其他运行,这可能是由于NFS缓存页面错误所致。(此外:在此安装过程中,该d_type字段是不可靠的,因为许多文件都显示为“未知”类型。)

command: getdents $bigdir
usr:0.08 sys:0.96  wall:280.79 CPU:0%
usr:0.06 sys:0.18  wall:0.25   CPU:97%
usr:0.05 sys:0.16  wall:0.21   CPU:99%
usr:0.04 sys:0.18  wall:0.23   CPU:98%
usr:0.05 sys:0.20  wall:0.26   CPU:99%
usr:0.04 sys:0.18  wall:0.22   CPU:99%
usr:0.04 sys:0.17  wall:0.22   CPU:99%
usr:0.04 sys:0.20  wall:0.25   CPU:99%
usr:0.06 sys:0.18  wall:0.25   CPU:98%
usr:0.06 sys:0.18  wall:0.25   CPU:98%
command: /bin/ls -f $bigdir
usr:0.53 sys:8.39  wall:8.97   CPU:99%
usr:0.53 sys:7.65  wall:8.20   CPU:99%
usr:0.44 sys:7.91  wall:8.36   CPU:99%
usr:0.50 sys:8.00  wall:8.51   CPU:100%
usr:0.41 sys:7.73  wall:8.15   CPU:99%
usr:0.47 sys:8.84  wall:9.32   CPU:99%
usr:0.57 sys:9.78  wall:10.36  CPU:99%
usr:0.53 sys:10.75 wall:11.29  CPU:99%
usr:0.46 sys:8.76  wall:9.25   CPU:99%
usr:0.50 sys:8.58  wall:9.13   CPU:99%

14
您是否可以在显示案件的时间上添加一个小的基准ls
伯恩哈德2014年

1
甜。而且,您可以添加一个选项来仅计算条目(文件)而不是列出它们的名称(为此列表节省了数百万的printf调用)。
ChuckCottrill 2014年

29
当您必须编写自定义代码以列出其内容时,您知道目录太大...
casey 2014年

1
@casey除非您不必这样做。所有关于getdentsvs的讨论都readdir没有抓住重点。
Mikel 2014年

9
来吧!它已经有500万个文件了。将您的自定义“ ls”程序放入其他目录。
2014年

12

速度慢的最可能原因是文件类型着色,可以通过选择\ls/bin/ls关闭颜色选项来避免这种情况。

如果目录中确实有很多文件,那么使用find也是一个不错的选择。


7
我不认为这应该被否决。排序是一个问题,但是即使不进行排序,ls -U --color也要花费很长时间,因为它将对stat每个文件进行排序。因此,两者都是正确的。
Mikel 2014年

关闭颜色会对的性能产生巨大影响,ls并且默认情况下,许多情况下都将其作为别名.bashrc
维克多·施罗德

是的/bin/ls -U,与等待很长时间之前相比,我做了a 并很快获得了输出
khebbie,

-3

我发现它的echo *工作速度比ls快得多。YMMV。


4
外壳将对进行排序*。因此,对于500万个文件,这种方式可能仍然很慢。
Mikel 2014年

3
@Mikel不仅如此,我很确定500万个文件已经超过了水珠将完全破裂的地步。
evilsoup 2014年

4
最小文件名长度(对于500万个文件)为3个字符(如果坚持使用更常见的字符,则为4个字符),再加上分隔符=每个文件4个字符,即20 MB的命令参数。这远远超过了普通的2MB扩展命令行长度。执行(甚至是内置函数)会失败。
2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.