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

Ram*_*esh 86 ls

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

Hau*_*ing 78

避免使用排序:

ls --sort=none # "do not sort; list entries in directory order"
Run Code Online (Sandbox Code Playgroud)

或者,等效地:

ls -U
Run Code Online (Sandbox Code Playgroud)

  • “-1”有很大帮助。"ls -f -1" 将避免任何 stat 调用并立即打印所有内容。列输出(发送到终端时的默认值)使其首先缓冲所有内容。在我的系统上,在包含 800 万个文件的目录中使用 btrfs(由“seq 1 8000000 | xargs touch”创建),“time ls -f -1 | wc -l”需要不到 5 秒,而“time ls -f -C | wc -l" 需要超过 30 秒。 (13认同)
  • 我想知道列布局也增加了多少开销。添加 `-1` 标志可能会有所帮助。 (10认同)

Ram*_*esh 53

ls实际上对文件进行排序并尝试列出它们,如果我们试图在目录中列出超过一百万个文件,这将成为一个巨大的开销。正如此链接中所述,我们可以使用stracefind列出文件。但是,由于我有 500 万个文件,这些选项对我的问题似乎也不可行。经过一番谷歌搜索后,我发现如果我们使用 列出目录getdents(),它应该会更快,因为ls,findPython库使用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);
}
Run Code Online (Sandbox Code Playgroud)

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

gcc  getdents.c -o getdents
./getdents
Run Code Online (Sandbox Code Playgroud)

计时示例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%
Run Code Online (Sandbox Code Playgroud)

  • 当您必须编写自定义代码来列出其内容时,您知道您的目录太大了...... (32认同)
  • 你能在时间上添加一个小的基准,你的案例用`ls`显示吗? (14认同)
  • 来吧!里面已经有 500 万个文件了。将您的自定义“ls”程序放入其他目录。 (9认同)

小智 16

最可能的原因是文件类型着色,您可以使用\ls/bin/ls关闭颜色选项来避免这种情况。

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

  • 我认为这不应该被否决。排序是一个问题,但即使不排序,`ls -U --color` 也需要很长时间,因为它会`stat` 每个文件。所以两者都是正确的。 (7认同)