膨大な数のファイルがあるディレクトリに対してlsコマンドが機能しない


70

500万のファイルがあるディレクトリがありました。lsこのディレクトリ内からコマンドを実行しようとすると、システムが大量のメモリを消費し、しばらくしてハングしました。lsコマンドを使用する以外にファイルをリストする効率的な方法はありますか?


11
あなたが別名を持っていないことを確認しlsているが、使用--colorまたは-Fそのようやっ意味しますlstat(2)ファイルごとに。
ステファンシャゼル14年

4
ところで、数百万のファイルを単一のディレクトリに保存するのは、かなり悪い考えです。ディレクトリレイアウトを制御する場合、おそらくいくつかの基準で分割しますか?
d33tah 14年

それは純粋なls電話でしたか、それともオプションを使用しましたか?
ホークレイジング14年

1
@ d33tahええ、500万人はたくさんです!私のルートファイルシステムには700万iノードの制限があります。
ミケル

7
500万個のアイテムを出力します-これをどのように見ていますか-単純なリストはあまりにも多く見えます。
user151019 14年

回答:


66

以下を使用してソートを回避します。

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

または、同等に:

ls -U

10
列レイアウトもどのくらいのオーバーヘッドを追加するのだろうか。-1フラグを追加すると役立ちます。
ミケル14年

おそらくそれほどではありませんが、少しでも役立つでしょうか?:)
ミケル

1
@Mikelそれは単なる推測ですか、それとも測定しましたか?私には-1もっと時間がかかるようです。
ホークレイジング14年

10
「-1」はかなり役立ちます。「ls -f -1」は、stat呼び出しを回避し、すべてを即座に出力します。列出力(端末に送信するときのデフォルト)により、すべてが最初にバッファリングされます。私のシステムでは、800万個のファイル(「seq 1 8000000 | xargs touch」によって作成される)があるディレクトリでbtrfsを使用すると、「time ls -f -1 | wc -l」は5秒未満、「time ls -f -C | wc -l "は30秒以上かかります。
スコットラム

1
@ToolmakerSteveデフォルトの動作(-Cstdoutが端末の-1場合、パイプの場合)は混乱を招きます。実験と測定を行うときは、出力の表示(コマンドが期待どおりに動作していることを確認するため)と出力の抑制(端末アプリケーションのスループットの交絡要因を回避するため)を切り替えます。より良いので、明示的に介して出力フォーマットを定義し、両方のモードで同じように動作するコマンドを使用するには-1-C-l、など
スコット・ラム

47

ls実際にファイルを並べ替えて一覧表示しようとしますが、ディレクトリ内に100万を超えるファイルを一覧表示しようとすると、大きなオーバーヘッドになります。このリンクで述べたように、straceまたはfindを使用してファイルをリストできます。ただし、これらのオプションは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マウントで約50万のファイルを含むディレクトリをリストするための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か?
ベルンハルト14年

1
甘い。また、名前をリストするのではなく、単純にエントリ(ファイル)をカウントするオプションを追加することもできます(このリストの場合、printfの呼び出しを何百万回も節約できます)。
ChuckCottrill 14年

29
あなたは...あなたはその内容を一覧表示するカスタムコードを記述する必要がある場合、ディレクトリが大きすぎる知っている
ケーシー

1
@caseyあなたがする必要がないことを除いて。getdents対vs についてのこのすべての話readdirは、ポイントを逃しています。
ミケル14年

9
いい加減にして!すでに500万のファイルがあります。カスタムの「ls」プログラムを他のディレクトリに配置します。
ヨハン14年

12

遅い理由として最も可能性が高いのはファイルタイプの色付けです。色のオプションを使用する\ls/bin/ls、オフにすることでこれを回避できます。

本当に多くのファイルがディレクトリにある場合、find代わりに使用することも良いオプションです。


7
私はこれがダウン投票されるべきではなかったと思う。並べ替えは1つの問題ですが、並べ替えを行わなくても、各ファイルを並べ替えるls -U --colorので時間がかかりstatます。両方とも正しいです。
ミケル14年

カラーリングをオフにすると、パフォーマンスに大きな影響があり、ls多く.bashrcの場合、デフォルトでエイリアスされます。
ビクターシュレーダー

うん、私はやった/bin/ls -Uし、時間がないの出力を持って、前に非常に長い時間を待っているに比べて
khebbie

-3

これecho *はlsよりもはるかに高速に動作します。YMMV。


4
シェルはをソートし*ます。したがって、この方法は、おそらく500万のファイルに対して非常に遅いです。
ミケル14年

3
@Mikelそれ以上に、500万個のファイルがグロビングが完全に壊れるポイントを超えていると確信しています。
evilsoup 14年

4
最小ファイル名の長さ(500万ファイル)は、3文字(より一般的な文字に固執する場合は4文字)と区切り文字=ファイルごとに4文字、つまり20 MBのコマンド引数です。これは、一般的な2MBの拡張コマンドラインの長さをはるかに超えています。Exec(およびビルトインさえ)がbaるでしょう。
ヨハン14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.