Как прочитать первые n-е файлы из каталога (пожалуйста, НЕ решение head -n)?

У меня есть каталог с более чем 60000 файлов. Как получить только N из них, не используя решения find | head -n или ls | head -n, так как find и ls чтение этого списка файлов занимает слишком много времени. Есть ли какие-нибудь конфиги для ls и find или есть еще программы, которые помогут сэкономить время?


person static    schedule 13.07.2014    source источник
comment
найти и прочитать этот список файлов занимает слишком много времени Сколько времени это занимает? 60000 файлов это не так уж и много. В моей системе ls | head -n ... | tail -n ... занимает гораздо меньше 1 с. Возможно, здесь нам не хватает информации: используете ли вы сетевую ФС? Или у вас были какие-то критические временные требования? Что-то другое?   -  person Sylvain Leroux    schedule 13.07.2014
comment
Можете ли вы предоставить требования к срокам и как долго работает то, что вы пробовали. Как написано, вопрос на самом деле не имеет особого смысла, поскольку вывод как ls, так и find будет линейно буферизован через канал, и вы не получите конвейерное решение намного быстрее, чем | head -n   -  person Reinstate Monica Please    schedule 13.07.2014


Ответы (4)


Для чего это стоит:

# Create 60000 files
sh$ for i in {0..100}; do
    for j in {0..600}; do
        touch $(printf "%05d" $(($i+$j*100)));
    done;
done

В Linux Debian Wheezy x86_64 с файловой системой ext4:

sh$ time bash -c 'ls | head -n 50000 | tail -10'
49990
49991
49992
49993
49994
49995
49996
49997
49998
49999

real    0m0.248s
user    0m0.212s
sys 0m0.024s


sh$ time bash -c 'ls -f | head -n 50000 | tail -10'
27235
02491
55530
44435
24255
47247
16033
45447
18434
35303

real    0m0.051s
user    0m0.016s
sys 0m0.028s


sh$ time bash -c 'find | head -n 50000 | tail -10'
./02491
./55530
./44435
./24255
./47247
./16033
./45447
./18434
./35303
./07658

real    0m0.051s
user    0m0.024s
sys 0m0.024s


sh$ time bash -c 'ls -f | sed -n 49990,50000p'
30950
27235
02491
55530
44435
24255
47247
16033
45447
18434
35303

real    0m0.046s
user    0m0.032s
sys 0m0.016s

Конечно, следующие два работают быстрее, так как они берут только первые записи (и прерывают процесс пары с помощью сломанного канала после того, как необходимые "строки" были прочитаны ):

sh$ time bash -c 'ls -f | sed 1000q >/dev/null'

real    0m0.008s
user    0m0.004s
sys 0m0.000s


sh$ time bash -c 'ls -f | head -1000>/dev/null'

real    0m0.008s
user    0m0.000s
sys 0m0.004s

Интересно (?), что с sed мы проводим время в процессе пользовательского пространства, а с head — в sys. После нескольких прогонов результаты стабильны...

person Sylvain Leroux    schedule 13.07.2014

Вы можете написать свою собственную простую утилиту на C.

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>

int main(int argc, char **argv) {
  DIR *dir;
  struct dirent *ent;
  int i = 0, n = 0;
  n = atoi(argv[2]);
  dir = opendir(argv[1]);
  while ((ent = readdir(dir)) != NULL) {
    if (strcmp(ent->d_name, ".") == 0 ||
        strcmp(ent->d_name, "..") == 0)
      continue;
    if (i++ >= n) break;
    printf("%s\n", ent->d_name);
  }
  closedir(dir);
  return 0;
}

Это всего лишь быстрый и грязный первый набросок, но вы поняли идею.

person ooga    schedule 13.07.2014
comment
Это, вероятно, не будет быстрее, чем find | head. Можете ли вы предоставить тайминги? - person tripleee; 14.07.2014
comment
@tripleee Если это касается вас, посчитайте время сами. Я не понимаю, как запуск двух программ с конвейером будет быстрее, чем одной программы. Однако я также понимаю, что find|head должно хватить. Дело в том, что приведенная выше программа проста и быстра в написании. - person ooga; 14.07.2014
comment
Ладно, чуть быстрее. Я думаю, что накладные расходы связаны с системой readdir() и что вы не можете обойти это узкое место, перейдя на чистый C, но, поскольку вы уже написали код, было бы интересно узнать, верна ли моя гипотеза. Но я не планирую делать замеры. - person tripleee; 14.07.2014

Вы можете использовать sed с q:

find ... | sed 10q  ## Prints 1st to 10th line.

Это заставило бы sed выйти после 10-й строки, что, вероятно, могло бы заставить find завершить свою функцию быстрее.

Другой способ — использовать awk, но sed все же более эффективен:

find ... | awk 'NR==11{exit}1'

Or

find ... | awk '1;NR==10{exit}'
person konsolebox    schedule 13.07.2014

ls -f directory | sed -n 1,10p       # print line 1-10

Вариант ls:

-f: не сортировать

person Cyrus    schedule 13.07.2014
comment
В зависимости от реализации это может по-прежнему позволять find продолжаться после 10-й строки, даже если sed больше не печатает свои выходные данные. - person konsolebox; 13.07.2014
comment
С вашим sed 10q это намного быстрее, чем sed -n 1,10p. ls -f быстрее, чем find. Протестировано с 60 000 копий /etc/passwd в одном каталоге. Протестировано с Ubuntu. - person Cyrus; 13.07.2014