2

Мне просто интересно, если кто-нибудь знает какие-либо лучшие практики или есть какие-либо документы по этой теме:

Сценарий поиска / поиска в файлах журнала. Чтобы высказать свою точку зрения, я буду использовать ls . Итак, допустим, что я запускаю ls чтобы вывести список файлов в каталоге

/var/log/remote/serverX.domain.local/ps/ps2.log.2014-mm-dd.gz

Где mm и dd являются числами месяца и дня, кроме serverX есть целый ряд серверов (для примера я использую 4,5,9,10 (это настоящие серверы)

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

   emartinez@serverlog:~$ time ls /var/log/remote/server{4,5,9,10}.domain.local/ps/ps2.log.2014-10-0{1,2}.gz
    /var/log/remote/server10.domain.local/ps/ps2.log.2014-10-01.gz  
    ...
    /var/log/remote/server5.domain.local/ps/ps2.log.2014-10-02.gz

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

Затем я заменяю последнюю фигурную скобку звездочкой:

time ls /var/log/remote/server{4,5,9,10}.domain.local/ps/ps2.log.2014-10-0*.gz

И я получаю следующую статистику:

    real      0m0.028s
    user      0m0.020s
    sys   0m0.020s

Это большая разница, хотя есть только 2 варианта, поскольку доступные даты - только 01 и 02 октября.

Я снова запустил тест, но на этот раз я заменил месяцы на список {1..12}, который соответствует результатам:

ps2.log.2014-{1..12}-0{1,2}.gz : real 0m0.010s
ps2.log.2014-{1..12}-0*.gz     : real 0m0.168s

Это большая разница только для одной звездочки !!! Имеет смысл, что это медленнее, но есть ли какие-то критерии относительно того, насколько медленнее, и есть ли какие-либо лучшие практики, изложенные где-нибудь?

2 ответа2

2

Может показаться, что prefix-* должен легко превращаться, например, в prefix-1 prefix-2 , поскольку мы привыкли видеть отсортированные списки каталогов. Но оказывается, что очень немногие файловые системы могут фактически создавать отсортированные списки имен файлов, и, кроме того, что не существует стандартного API для запроса отсортированных списков имен файлов.

Если программе - такой как ls , или, в этом отношении, bash - нужен список имен файлов, ей нужно прочитать весь список каталогов, который будет создан в некотором случайном порядке (часто порядок связан со временем создания иногда оно основано на хэше имени файла, но ни в коем случае это не простой алфавитный порядок). Таким образом, чтобы разрешить prefix-* , вам нужно прочитать весь каталог и проверить каждое имя файла по шаблону. Поскольку наиболее дорогостоящей частью этой процедуры является чтение каталога, не имеет значения, насколько сложен шаблон или сколько имен файлов соответствуют шаблону.

Таким образом, расширение пути ("решение глобусов") будет медленным в большом каталоге. Это причина избегать больших каталогов, а не причина избегать глобусов.

Но есть еще один важный пункт данных: prefix-{1,2} не является расширением пути. Это « расширение скобок » и расширение стандарта оболочки Posix (хотя его реализуют почти все оболочки). Существует ряд различий между расширением фигурных скобок и расширением пути, но одно важное и важное различие заключается в том, что расширение фигурных скобок не зависит от существования файлов. Разбивка скобок - это простая строковая операция.

Следовательно, prefix-{1,2} всегда будет расширяться до prefix-1 prefix-2 , независимо от того, существуют эти файлы или нет. Это означает, что он может быть расширен без чтения каталога и без stat какого-либо файла. Понятно, что это будет быстро. Но есть и обратная сторона: невозможно определить, соответствует ли результат реальным файлам.

Рассмотрим следующий простой пример:

$ mkdir test && cd test
$ touch file1 file2 file4
$ ls file*
file1 file2 file4
$ ls file[1234]
file1 file2 file4
$ ls file{1,2,3,4}
ls: cannot access file3: No such file or directory
file1 file2 file4

Конечная точка: расширение имени пути выполняется оболочкой, а не ls . С расширением пути мы могли бы также использовать echo:

$ echo file*
file1 file2 file4
$ echo file[1234]
file1 file2 file4

И echo будет производить список несколько быстрее, потому что все echo нужно сделать , это печатать свои аргументы, а ls (который принимает те же аргументы) должен stat каждый аргумент для того , чтобы убедиться , что это файл. Эта stat - которая не является дешевым вызовом - полностью избыточна в случае расширения пути, потому что оболочка уже использовала список каталогов для фильтрации списка файлов и, следовательно, известно, что каждое имя файла, переданное в ls , существует , (Если глобус не совпадал ни с одним файлом.)

Кроме того, echo является встроенным в bash , поэтому его можно вызывать без создания дочернего процесса.

В случае расширения скобок, однако, echo не дает тот же результат:

$ echo file{1,2,3,4}
file1 file2 file3 file4

Таким образом, мы могли бы использовать ls , перенаправляя вывод ошибок в область битов:

$ ls file{1,2,3,4}
file1 file2 file4

и в этом случае вызовы stat не являются избыточными, потому что оболочка никогда не проверяла имена файлов.

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

Например, вместо путей вроде:

/var/log/remote/serverX.domain.local/ps/ps2.log.2014-mm-dd.gz

Вы могли бы использовать:

/var/log/remote/serverX/domain.local/ps/ps2.log.2014-mm-dd-gz

И если вы храните журналы вечно, вы можете извлечь год, чтобы избежать бесконечно увеличивающегося размера каталога:

/var/log/remote/2014/serverX/domain.local/ps/ps2.log.2014-mm-dd-gz

(2014 намеренно повторяется.)

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

/var/log/remote/server[2357].domain.local/ps/ps2.log.2014-10-*-gz

но это может оптимизировать

/var/log/remote/server[2357]/domain.local/ps/ps2.log.2014-10-*-gz

Во втором случае server[2357] нужно сопоставлять только с именами каталогов, и как только это будет сделано, ps2.log.2014-10-*-gz нужно сопоставлять только с именами файлов в сопоставленных каталогах.

1

Расширение оболочки всегда выполняется в определенном порядке; Расширение скобок выполняется первым, расширение имени файла выполняется последним.

Таким образом, команда как

echo {1..3}*

сначала расширяется до

echo 1* 2* 3*

затем выполняется расширение имени файла для 1* , 2* и 3* . Каждое расширение включает в себя просмотр всех имен файлов в каталоге и сравнение их с шаблоном.

По мере роста количества слов и / или количества файлов в каталоге это становится постепенно медленнее. Даже в пустой директории,

shopt -s nullglob  # print nothing for non-matching words
echo {1..1000000}* # prints nothing
shopt -u nullglob  # back to the default

занимает почти пять секунд на моей машине. Это совсем не удивительно, если учесть, что расширение имени файла выполняется миллион раз ...

Гораздо более быстрой альтернативой является, по возможности, избегать объединения обоих типов расширения оболочки

Команда

echo [1-1000000]* # also prints nothing

ищет те же имена файлов, но использует один шаблон. Это займет 33 миллисекунды на моей машине.

Использование квадратных скобок вместо фигурных скобок имеет дополнительные преимущества:

$ touch 13
$ echo {1..20}*
13 13
$ echo [1..20]*
13

Первый подход нашел файл дважды, так как он соответствует шаблонам 1* и 13* . Этого не происходит с "чистым" расширением имени файла.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .