คำสั่ง ls ไม่ทำงานสำหรับไดเรกทอรีที่มีไฟล์จำนวนมาก


70

ฉันมีไดเรกทอรีซึ่งมีไฟล์ประมาณ5 ล้านไฟล์ เมื่อฉันพยายามเรียกใช้lsคำสั่งจากภายในไดเรกทอรีนี้ระบบของฉันใช้หน่วยความจำจำนวนมากและจะหยุดทำงานหลังจากนั้นไม่นาน มีวิธีที่มีประสิทธิภาพในการแสดงรายการไฟล์นอกเหนือจากการใช้lsคำสั่งหรือไม่?


11
ตรวจสอบให้แน่ใจว่าคุณไม่มีนามแฝงสำหรับการlsใช้งานนั้น--colorหรือ-Fหมายความว่าทำlstat(2)สำหรับแต่ละไฟล์
Stéphane Chazelas

4
อย่างไรก็ตามการจัดเก็บไฟล์หลายล้านไฟล์ในไดเรกทอรีเดียวเป็นความคิดที่ไม่ดี หากคุณควบคุมเค้าโครงไดเรคทอรีคุณอาจแยกมันตามเกณฑ์บางอย่างหรือไม่
d33tah

นั่นเป็นการlsโทรที่แท้จริงหรือคุณใช้ตัวเลือกหรือไม่?
Hauke ​​Laging

1
@ d33tah ใช่ 5 ล้านมีจำนวนมาก! ระบบไฟล์ root ของฉันมีขีด จำกัด 7 ล้าน inodes
Mikel

7
รายการที่จะส่งออก 5 ล้านรายการ - วิธีที่คุณดูนี้ - รายการง่ายมากเกินไปที่จะเห็น - ดังนั้นสิ่งที่คุณต้องการรายชื่อ?
user151019

คำตอบ:


66

หลีกเลี่ยงการเรียงลำดับโดยใช้:

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

หรือเทียบเท่า:

ls -U

10
ฉันสงสัยว่าค่าใช้จ่ายในเค้าโครงคอลัมน์จะเพิ่มด้วยเช่นกัน การเพิ่มการ-1ตั้งค่าสถานะสามารถช่วยได้
Mikel

อาจจะไม่มาก แต่ก็ช่วยได้ใช่ไหม :)
Mikel

1
@ Mikel นั่นเป็นเพียงการคาดเดาหรือคุณวัดได้หรือไม่? สำหรับฉันดูเหมือนว่า-1จะใช้เวลานาน
Hauke ​​Laging

10
"-1" ช่วยได้ไม่น้อย "ls -f -1" จะหลีกเลี่ยงการโทรสถิติและพิมพ์ทุกอย่างทันที เอาต์พุตคอลัมน์ (ซึ่งเป็นค่าเริ่มต้นเมื่อส่งไปยังเทอร์มินัล) ทำให้มันบัฟเฟอร์ทุกอย่างก่อน ในระบบของฉันการใช้ btrfs ในไดเรกทอรีที่มีไฟล์ 8 ล้านไฟล์ (สร้างโดย "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จริง ๆ แล้วเรียงลำดับไฟล์และพยายามที่จะแสดงรายการพวกเขาซึ่งจะกลายเป็นค่าใช้จ่ายมากถ้าเราพยายามที่จะแสดงรายการมากกว่าหนึ่งล้านไฟล์ในไดเรกทอรี ตามที่กล่าวไว้ในลิงค์นี้เราสามารถใช้straceหรือfindเพื่อแสดงรายการไฟล์ อย่างไรก็ตามตัวเลือกเหล่านี้ดูเหมือนจะไม่สามารถแก้ปัญหาของฉันได้เนื่องจากฉันมีไฟล์ 5 ล้านไฟล์ หลังจากบิตของบาง googling ผมพบว่าถ้าเรารายการไดเรกทอรีที่ใช้getdents()ก็ควรจะเป็นได้เร็วขึ้นเพราะls, findและPythonห้องสมุดใช้readdir()ซึ่งจะช้า แต่ใช้getdents()ภายใต้

เราสามารถค้นหารหัส C เพื่อแสดงรายการไฟล์ที่ใช้getdents()จากที่นี่ :

/*
 * 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อยู่กับการกำหนดค่าระบบ นี่คือบางส่วนของเวลาที่แสดงให้เห็นถึงการเพิ่มความเร็ว 40x สำหรับการแสดงรายการไดเรกทอรีที่มีไฟล์ประมาณ 500k ผ่านเมาต์ NFS ในคลัสเตอร์การคำนวณ แต่ละคำสั่งก็วิ่ง 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หรือไม่?
แบร์นฮาร์ด

1
หวาน. และคุณสามารถเพิ่มตัวเลือกในการนับรายการ (ไฟล์) แทนที่จะแสดงรายชื่อ (บันทึกการโทรนับล้านไปยัง printf สำหรับรายชื่อนี้)
ChuckCottrill

29
คุณรู้ว่าไดเรกทอรีของคุณมีขนาดใหญ่เกินไปเมื่อคุณต้องเขียนรหัสที่กำหนดเองเพื่อแสดงรายการเนื้อหา ...
casey

1
@casey ยกเว้นคุณไม่จำเป็นต้อง ทั้งหมดนี้พูดคุยเกี่ยวกับgetdentsvs readdirคิดถึงจุด
Mikel

9
มาเลย! มีไฟล์อยู่ 5 ล้านไฟล์แล้ว วางโปรแกรม "ls" ที่กำหนดเองของคุณลงในไดเรกทอรีอื่น
Johan

12

สาเหตุที่เป็นไปได้มากที่สุดที่ทำให้ช้าคือการระบายสีชนิดไฟล์คุณสามารถหลีกเลี่ยงปัญหานี้ได้ด้วยการปิด\lsหรือ/bin/lsปิดตัวเลือกสี

หากคุณมีไฟล์จำนวนมากจริงๆใน dir การใช้findแทนก็เป็นตัวเลือกที่ดีเช่นกัน


7
ฉันไม่คิดว่ามันควรจะถูกลดระดับลง การเรียงลำดับเป็นปัญหาหนึ่ง แต่ถึงแม้จะไม่มีการเรียงลำดับls -U --colorก็อาจใช้เวลานานเนื่องจากstatแต่ละไฟล์ ดังนั้นทั้งสองถูกต้อง
Mikel

การเปลี่ยนสีออกมีผลกระทบอย่างมากต่อประสิทธิภาพการทำงานของlsและเป็น aliased โดยเริ่มต้นในหลาย.bashrcs ออกมี
Victor Schröder

ใช่ฉันทำ/bin/ls -Uและได้ผลผลิตในเวลาไม่นานเมื่อเทียบกับการรอเป็นเวลานานก่อน
khebbie

-3

ฉันพบว่าecho *ทำงานได้เร็วกว่า ls มาก YMMV


4
*เปลือกจะจัดเรียง ดังนั้นวิธีนี้อาจจะช้ามากสำหรับไฟล์ 5 ล้านไฟล์
Mikel

3
@Mikel ยิ่งไปกว่านั้นฉันค่อนข้างมั่นใจว่ามีไฟล์ 5 ล้านไฟล์อยู่เหนือจุดที่วงกลมจะแตกสลายอย่างสิ้นเชิง
evilsoup

4
ความยาวชื่อไฟล์ขั้นต่ำ (สำหรับไฟล์ 5 ล้านไฟล์) คือ 3 ตัวอักษร (อาจเป็น 4 ถ้าคุณใช้ตัวอักษรทั่วไป) บวกตัวคั่น = 4 ตัวอักษรต่อไฟล์เช่น 20 MB ของอาร์กิวเมนต์คำสั่ง นั่นคือความยาวบรรทัดคำสั่งขยายทั่วไป 2MB Exec (และแม้กระทั่ง builtins) จะหยุดชะงัก
Johan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.