У меня есть каталог с более чем 60000 файлов. Как получить только N из них, не используя решения find | head -n
или ls | head -n
, так как find
и ls
чтение этого списка файлов занимает слишком много времени. Есть ли какие-нибудь конфиги для ls
и find
или есть еще программы, которые помогут сэкономить время?
Как прочитать первые n-е файлы из каталога (пожалуйста, НЕ решение head -n)?
Ответы (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. После нескольких прогонов результаты стабильны...
Вы можете написать свою собственную простую утилиту на 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;
}
Это всего лишь быстрый и грязный первый набросок, но вы поняли идею.
find | head
. Можете ли вы предоставить тайминги?
- person tripleee; 14.07.2014
find|head
должно хватить. Дело в том, что приведенная выше программа проста и быстра в написании.
- person ooga; 14.07.2014
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}'
ls -f directory | sed -n 1,10p # print line 1-10
Вариант ls
:
-f
: не сортировать
find
продолжаться после 10-й строки, даже если sed
больше не печатает свои выходные данные.
- person konsolebox; 13.07.2014
sed 10q
это намного быстрее, чем sed -n 1,10p
. ls -f
быстрее, чем find
. Протестировано с 60 000 копий /etc/passwd в одном каталоге. Протестировано с Ubuntu.
- person Cyrus; 13.07.2014
ls | head -n ... | tail -n ...
занимает гораздо меньше 1 с. Возможно, здесь нам не хватает информации: используете ли вы сетевую ФС? Или у вас были какие-то критические временные требования? Что-то другое? - person Sylvain Leroux   schedule 13.07.2014ls
, так иfind
будет линейно буферизован через канал, и вы не получите конвейерное решение намного быстрее, чем| head -n
- person Reinstate Monica Please   schedule 13.07.2014