1

Задача такая - имеется один скрипт, который постоянно складывает файлы в каталог kat1. Второй скрипт должен постоянно брать случайный файл из kat1, обрабатывать его (sed'ом) и скидывать обработанную версию файла в другой каталог kat2.

Возник вопрос: какая конструкция может быстро получать имя случайного файла в каталоге.
find здесь наверно не пойдет, потому что файлы постоянно добавляются новые файлы...

2 (25.01.2013 03:32:07 отредактировано xxblx)

valet пишет:

Возник вопрос: какая конструкция может быстро получать имя случайного файла в каталоге.

#!/bin/sh
array=($(ls))
len=${#array[*]}
rand=$[ $RANDOM % $len ]
echo ${array[$rand]}
Fedora GNOME3

3 (25.01.2013 03:52:36 отредактировано valet)

xxblx, ого. Спасибо конечно за вариант, но как-то слишком в лоб мне кажется. Мне кажется это будет тормозить весь процесс...
Может тогда уже лучше что-то типа:

find . -maxdepth 1 -mmin -10|shuf -n1|cut -f2 -d"/"

здесь
find . -maxdepth 1 -mmin -10 - найдет все файлы в текущем каталоге, измененные за последние 10 мин.
shuf -n1 - выбор случайной строки-файла
cut -f2 -d"/" - вырезаем имя, так как find возвращает файлы в формате ./file

И то я за find переживаю, даст ли ускорение в плане поиска ключ -mmin? Может есть еще что быстрее?

Или тогда уже через ls, то тогда уже

ls|shuf -n1

Хоть не надо строить никакой массив.

Вот что быстрее будет ls или find с ограничением по ключу -mmin?

4

valet пишет:

но как-то слишком в лоб мне кажется.

вопрос был поставлен так: "какая конструкция может быстро получать имя случайного файла в каталоге." - на него я и дал ответ в силу своих знаний (а они у меня невелики). а дальше уж сам смотри подходит он тебе или нет.

valet пишет:

Вот что быстрее будет ls или find с ограничением по ключу -mmin?

сделай сначала

time find . -maxdepth 1 -mmin -10|shuf -n1|cut -f2 -d"/"

а потом

time ls|shuf -n1

и сравни результаты bx

Fedora GNOME3

5

xxblx пишет:

вопрос был поставлен так: "какая конструкция может быстро получать имя случайного файла в каталоге."

Да. Возможно мне надо было четче уточнить, что ключевое здесь "быстро", так как методов и так понятно что много. В первую очередь я ожидал что может даже есть какая-то утилитка малоизвестная которая выдает имя случайного файла в каталоге и работает скажем как-то системными вызовами из-за чего очень быстро...

xxblx пишет:

на него я и дал ответ

Да, я же поблагодарил. Еще раз спасибо ))

По поводу быстродействия - find намного быстрее таки:

valet@valet-comp:/files$ ls |wc -l
1138276
valet@valet-comp:/files$ time find . -maxdepth 1 -mmin -10|shuf -n1|cut -f2 -d"/"

real    0m1.781s
user    0m0.348s
sys    0m1.432s
valet@valet-comp:/files$ time ls|shuf -n1
temp-1358240990.html

real    0m6.643s
user    0m6.136s
sys    0m0.572s

6

valet пишет:

По поводу быстродействия - find намного быстрее таки:

Я бы по два-три раза подряд повторил тест на скорость, тк при первом поиске наверняка читалось с диска, а при втором уже из кэша.

История показывает, что во всем новом обычно кроется какой-то подвох.
Классическая ошибка, которую совершают проектировщики
абсолютно надежных систем, - недооценка изобретательности клинических идиотов.

7

А если Python попробовать?

#!/usr/bin/env python
#-*- coding: utf-8 -*-

from os import listdir
from random import choice
print choice(listdir('.'))
Да, у меня Ubuntu.
Нет, не глючит.

8

-=Serj=- пишет:

А если Python попробовать?

Это фантастика, на каталоге в миллион файлов Python показывает скорость в 5 раз выше find'а. Всунул ваш срипт в testperl:

 valet@valet-comp:~/home$ ls files|wc -l
1138276
 valet@valet-comp:~/home$ time find files -maxdepth 1 -mmin -10|shuf -n1|cut -f2 -d"/"

real    0m3.428s
user    0m0.348s
sys    0m2.716s
 valet@valet-comp:~/home$ time ./testperl 
temp-1358312639.html

real    0m0.737s
user    0m0.156s
sys    0m0.556s
 valet@valet-comp:~/home$ 

В чем секрет, откуда такая скорость у питона? Он что как-то принципиально по-другому работает с файловой системой или диском?

9 (27.01.2013 11:05:06 отредактировано -=Serj=-)

Скорее всего, Python скрипт не быстрее find, но быстрее цепочки пайпов.
ЗЫ. И да, тест повторяли несколько раз для чистоты эксперимента?

Да, у меня Ubuntu.
Нет, не глючит.

10

-=Serj=- пишет:

ЗЫ. И да, тест повторяли несколько раз для чистоты эксперимента?

Да, конечно, время менялось, но менялось там и там, и каждый раз питон был в ~6раз быстрее, чем конструкция с find.

-=Serj=- пишет:

Скорее всего, Python скрипт не быстрее find, но быстрее цепочки пайпов.

Может быть, но почему тогда он быстрее чем

ls|shuf -n1

11

valet пишет:

Может быть, но почему тогда он быстрее чем ls|shuf -n1

в порядке затрат времени:
1) ls сортирует по алфавиту
2) тормоза в трубе
3) накладные расходы на exec() и подгрузку либ

valet пишет:

valet@valet-comp:/files$ time find . -maxdepth 1 -mmin -10|shuf -n1|cut -f2 -d"/"

правильно было бы измерять так: time bash -c 'find . -maxdepth 1 -mmin -10|shuf -n1|cut -f2 -d"/"'
иначе относительно правильным будет только real-время
ЗЫ: если большинство файлов относительно новые, то mmin будет сильно тормозить...

95% процентов проблем находятся между клавиатурой и стулом.

12

Fat-Zer пишет:

иначе относительно правильным будет только real-время

А я на него и смотрел ))

Fat-Zer пишет:

ЗЫ: если большинство файлов относительно новые, то mmin будет сильно тормозить...

не заметил подобного.

13

valet пишет:

не заметил подобного.

в 2,5 раза разница:

alexander@goblin /tmp/tst $ time find /tmp/tst/ -maxdepth 1 >/dev/null

real    0m0.393s
user    0m0.219s
sys     0m0.172s
alexander@goblin /tmp/tst $ time find /tmp/tst/ -maxdepth 1 -mmin 1000 >/dev/null

real    0m1.070s
user    0m0.184s
sys     0m0.882s
alexander@goblin /tmp/tst $ find /tmp/tst/ -maxdepth 1 |wc -l
940212

и это в tmpfs всё... на железках разница будет несколько меньше, но всё равно существенна:

alexander@goblin ~/tst $ echo 3 | sudo tee /proc/sys/vm/drop_caches
3
alexander@goblin ~/tst $ time find . -maxdepth 1 >/dev/null

real    0m39.109s
user    0m0.235s
sys     0m0.456s
alexander@goblin ~/tst $ time find . -maxdepth 1 >/dev/null

real    0m0.495s
user    0m0.213s
sys     0m0.280s
alexander@goblin ~/tst $ echo 3 | sudo tee /proc/sys/vm/drop_caches
3
alexander@goblin ~/tst $ time find . -maxdepth 1 -mmin 1000 >/dev/null

real    0m56.808s
user    0m0.373s
sys     0m4.153s
alexander@goblin ~/tst $ time find . -maxdepth 1 -mmin 1000 >/dev/null

real    0m1.100s
user    0m0.194s
sys     0m0.902s
alexander@goblin ~/tst $ find . -maxdepth 1 |wc -l
940213

кстати без кеша разница с питоном минимальная, а с кешами меньше, чем в 1,5 раза...

alexander@goblin ~/tst $ echo 3 | sudo tee /proc/sys/vm/drop_caches
3
alexander@goblin ~/tst $ time bash -c "find . -maxdepth 1 | shuf -n1"
./692426270

real    0m30.249s
user    0m0.274s
sys     0m0.519s
alexander@goblin ~/tst $ time bash -c "find . -maxdepth 1 | shuf -n1"
./30414668106

real    0m0.521s
user    0m0.227s
sys     0m0.304s
alexander@goblin ~/tst $ echo 3 | sudo tee /proc/sys/vm/drop_caches
3
alexander@goblin ~/tst $ time ./my.py
15321712054

real    0m29.831s
user    0m0.088s
sys     0m0.477s
alexander@goblin ~/tst $ time ./my.py
19621382664

real    0m0.318s
user    0m0.087s
sys     0m0.229s
95% процентов проблем находятся между клавиатурой и стулом.

14 (29.01.2013 02:41:27 отредактировано valet)

То есть делаем выводы, что как не крути - Питон быстрее ))
Это кстати все равно интересно ))

15

valet пишет:

То есть делаем выводы, что как не крути - Питон быстрее ))
Это кстати все равно интересно ))

и на перловке наверняка быстрее получится, а на си ещё быстрее...
но если весь код на баше, то к таким ухищрениям надо прибегать только если скорость неудовлетворительно и доказано, что узкое место именно здесь... перфекционизм совершенно излишен...
кстати ls -f показывает себя лучше find'а...

95% процентов проблем находятся между клавиатурой и стулом.

16 (30.01.2013 12:08:46 отредактировано valet)

Fat-Zer пишет:

и на перловке наверняка быстрее получится

еще бы примерчик был бы в самый раз, а вдруг ab

Fat-Zer пишет:

но если весь код на баше, то к таким ухищрениям надо прибегать только если скорость неудовлетворительно и доказано, что узкое место именно здесь

да, именно узкое место вот такая вот выборка случайного файла, дальше идет обработка этого случайного файла sed'ом - и это уже быстро, так как работа с одним конкретным файлом (ну а даже если не быстро - это уже никак не ускорить). Почему надо выбирать именно случайный - дело в том, что каталог постоянно в онлайне пополняется новыми файлами, то есть пройтись по определенному списку никак не получится, приходится постоянно получать с каталога случайный.

Fat-Zer пишет:

кстати ls -f показывает себя лучше find'а...

действительно, протестил, так и есть. Но Python еще быстрее - вот в чем фишка ab

Еще раз все тесты 2 раза на каталоге в более 1 млн файлов:

+ открыть спойлер

valet@valet-comp:$:~/data/files/temp$ ls |wc -l
1138276
valet@valet-comp:$:~/data/files/temp$ time ls|shuf -n1
temp-1358284308.html

real    0m6.337s
user    0m5.840s
sys    0m0.556s
valet@valet-comp:$:~/data/files/temp$ time find . -maxdepth 1 -mindepth 1 -type f -mmin -100000|shuf -n1|cut -f2 -d"/"
temp-1358279553.html

real    0m1.954s
user    0m0.568s
sys    0m1.444s
valet@valet-comp:$:~/data/files/temp$ time ls -f|shuf -n1
temp-1358338858.html

real    0m0.887s
user    0m0.488s
sys    0m0.448s
valet@valet-comp:$:~/data/files/temp$ time ../../rand_file_python
temp-1358293655.html

real    0m0.555s
user    0m0.156s
sys    0m0.400s
valet@valet-comp:$:~/data/files/temp$ time ls|shuf -n1
temp-1358275701.html

real    0m7.476s
user    0m6.264s
sys    0m0.560s
valet@valet-comp:$:~/data/files/temp$ time find . -maxdepth 1 -mindepth 1 -type f -mmin -100000|shuf -n1|cut -f2 -d"/"
temp-1358414618.html

real    0m1.906s
user    0m0.516s
sys    0m1.444s
valet@valet-comp:$:~/data/files/temp$ time ls -f|shuf -n1
temp-1358290731.html

real    0m0.878s
user    0m0.428s
sys    0m0.496s
valet@valet-comp:$:~/data/files/temp$ time ../../rand_file_python
temp-1358248393.html

real    0m0.568s
user    0m0.128s
sys    0m0.440s

Кстати как видим я специально для find взял mmin большое, чтобы в критерий попадали заведомо все файлы. Все равно find быстр, намного быстрее обычного ls, правда ls -f уже быстрее find, но python несомненный лидер, как видим, он обыгрывает по скорости обычный ls чуть не в 15 раз и быстрее ls -f в 1.5 раза. Для меня кстати до сих пор загадка почему такое происходит, очевидно питон как-то по другому делает системные вызовы к ФС или диску - не могу точно знать, не силен в таких деталях.
Но для моей задачи этот питон более чем, полезен, поэтому большое спасибо -=Serj=- за наводку.

17 (30.01.2013 12:47:09 отредактировано valet)

Чтобы не создавать новую тему, хотелось бы еще кое что спросить...

Вот мой скрипт, который обрабатывает файлы (с помощью sed-скрипта script) с большого каталога html_in (в него постоянно другим скриптом докладываются файлы) и складываются в обработанном виде в другой каталог html_out, удаляя в исходном этот файл:

#!/bin/bash

cd html_in/

while [ `../rand_file_python|wc -l` -gt 0 ]
do
file=`../rand_file_python`
../script $file > ../html_out/$file
rm -f $file
done

cd ../

Выборка случайного файла из каталога происходит питоновским скриптом, предложенном в этой теме rand_file_python:

#!/usr/bin/env python
#-*- coding: utf-8 -*-

from os import listdir
from random import choice
print choice(listdir('.'))

Но мне в моем скрипте не нравится то, что приходится 2 раза вызывать выборку случайного файла: один раз для while для проверки условия продолжения обработки цикла, второй раз уже непосредственно для получения случайного файла.
Кто подскажет как это правильно обойти? Думаю можно как-то обойтись одним вызовом с присовением в переменную, но как это правильно сделать в условии обхода цикла?

П.С. Гугление по сабжу "bash while переменная в условии" ничего мне не подсказало, видать не правильно гуглю ))

18

Использовать что-то, вроде

while True
do
file = `../rand_file_python`
if file не существует
    then break
script
done
Да, у меня Ubuntu.
Нет, не глючит.

19

-=Serj=- пишет:

if file не существует

Это можно, но тогда лишней будет конструкция if file не существует, хотя может это уже попроще будет - все таки не выборка случайного файла.
Попробую пока так...

20

valet пишет:

но тогда лишней будет конструкция if file не существует

так она уже итак присутствует:

valet пишет:

`../rand_file_python|wc -l` -gt 0

Да, у меня Ubuntu.
Нет, не глючит.

21

-=Serj=- пишет:

так она уже итак присутствует:

Вот именно, а я хочу чтобы она не присутсвовала ab
Ну я же в любом случае выбираю случайный файл - без этого никак, а раз выбираю то можно как-то использовать результаты этой операции 1 раз.

22

Брр.. Проблема в вашем скрипте в том, что поиск случайного файла выполняется два раза за итерацию:

valet пишет:

while [ `../rand_file_python|wc -l` -gt 0 ]
do
file=`../rand_file_python`

В моём же варианте цикла -  1 раз. Не?

Да, у меня Ubuntu.
Нет, не глючит.

23

-=Serj=- пишет:

Брр.. Проблема в вашем скрипте в том, что поиск случайного файла выполняется два раза за итерацию:

Да.

-=Serj=- пишет:

В моём же варианте цикла -  1 раз. Не?

Верно, но у вас появляется дополнительная конструкция - проверка файла на существование - пока не знаю насколько тяжела эта операция, но  в любом случае по возможности хотелось бы использовать результат выборки случайного файла - в любом случае это уже есть...

24 (30.01.2013 16:18:15 отредактировано valet)

Но спасибо за идею, привел скрипт к такому виду, мне кажется уже оптимальнее не бывает:

#!/bin/bash

cd html_in/

while true
do
file=`../rand_file_python`
if [ -z $file ] ; then break ; fi
../script $file > ../html_out/$file
rm -f $file
done

cd ../

Единственное все таки проще проверить переменную на существование чем заставлять дергать диск на проверку наличие файла.

П.С. Визуально отрабатываться стало в 2 раза быстрее ab

25

valet пишет:

Кстати как видим я специально для find взял mmin большое, чтобы в критерий попадали заведомо все файлы.

само наличие проверок mmin, как и type сильно тормозят т.к. они вызываются для каждого файла в отдельности...

valet пишет:

Вот мой скрипт, который обрабатывает файлы (с помощью sed-скрипта script) с большого каталога html_in (в него постоянно другим скриптом докладываются файлы) и складываются в обработанном виде в другой каталог html_out, удаляя в исходном этот файл:

вот расскажи, _НАФИГА_ [прошу прощения за капс] тут нужна выборка _случайного_ файла!?
сколько раз говорить людям, чтобы они полную задачу давали, а не спрашивали, как осуществить идиотское псевдо-решение...

#!/bin/bash

list=( $(find . -maxdepth 1 -type f) )

while [ -n "${list[0]}" ]; do
    for f in "${list[@]}"; do
        ../script "$f" > "../tst2/$f"
        rm "$f"
    done

    list=( $(find . -maxdepth 1 -type f) )
done

пришло 9000 файлов - обработал... потом следующую порцию, что пришла пока эти обрабатываешь итд...
если файлов много, то и памяти кушать будет много - если это критично. то используй внешние списки...

если ну уж очень хочется элемент случайности привнести, то выбирай по 10000 файлов за раз...
а вообще случайность нужна только для криптографии и лотерей...

95% процентов проблем находятся между клавиатурой и стулом.

26

Fat-Zer пишет:

вот расскажи, _НАФИГА_ [прошу прощения за капс] тут нужна выборка _случайного_ файла!?

Тоже об этом подумал:)

Да, у меня Ubuntu.
Нет, не глючит.

27

Fat-Zer пишет:

сколько раз говорить людям, чтобы они полную задачу давали

Да, н ов первом посте как раз и была сформулирована полная задача ))

Fat-Zer, да, ты прав, об этом не подумал. Твое решение самое правильное. Обрабатывать можно пачками.
Спасибо, сделал себе так, все летает.

28

-=Serj=- пишет:

Тоже об этом подумал:)

Да, ребята, вы правы. Собственно случайный выбор задумывался из-за того что каталог постоянно нарастает. А вот про пакетную обработку я как-то не подумал.

29 (01.02.2013 16:35:23 отредактировано -=Serj=-)

valet пишет:
Fat-Zer пишет:

сколько раз говорить людям, чтобы они полную задачу давали

Да, н ов первом посте как раз и была сформулирована полная задача ))

Дык, вы там указали не полную задачу, а своё представление о её решении:

valet пишет:

скрипт должен постоянно брать случайный файл

Ну и мой вариант

#!/usr/bin/env python
#-*- coding: utf-8 -*-

from os import listdir, remove
from subprocess import call

while True:
    files = listdir('.')
    if len(files) > 0:
        for f in files:
            err = call('../script %s > ../tst2/%s' % (f, f), shell = True)
            if err == 0:
                remove(f)
    else:
        break
Да, у меня Ubuntu.
Нет, не глючит.

30

-=Serj=- пишет:

Ну и мой вариант

попробовал, у меня выбивает ошибку:

Traceback (most recent call last):
  File "./obrabotka", line 11, in <module>
    call('script %s > html_out/%s' % f, shell = True)
TypeError: not enough arguments for format string

Так как питона не знаю вообще понятия не имею, что оно от меня хочет.

Кстати тут по скрипту получается что он должен находится в том же каталоге что и файлы, но также нельзя, иначе рано или поздно сам скрипт удалится как один из файлов. То есть тут должен быть переход в каталог обработки, на bash'е это cd html_in для моего случая, как это будет на питоне понятия не имею, может поэтому и ошибка, потому что я привел скрипт к виду:

#!/usr/bin/env python
#-*- coding: utf-8 -*-

from os import listdir, remove
from subprocess import call

while True:
    files = listdir('html_in')
    if len(files) > 0:
        for f in files:
            call('script %s > html_out/%s' % f, shell = True)
            remove(f)
    else:
        break