2

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

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

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

Один из способов сделать это состоит из нескольких шагов, например:

awk -F "|" '{ print $5 }' trace.log | sort | uniq | xargs -l sh -c 'echo -n $0 && grep "$0" trace.log | wc -c'

где awk извлекает токен (5-я запись с помощью '|'), сортировка | uniq извлекает список уникальных токенов, появляющихся в файле, и, наконец, xargs greps и подсчитывает байты.

Теперь это работает, но это ужасно неэффективно, потому что один и тот же (огромный) файл получает grep X раз.

Есть ли более разумный способ добиться того же с помощью команд оболочки? (где под «умнее» я подразумеваю более быструю и не требующую тонны ОЗУ или временную память, например, сортировку всего файла в ОЗУ или сортировку в файл tmp).

1 ответ1

2

Пытаться:

awk -F "|" '{ a[$5]+=1+length($0) } END{for (name in a) print name,a[name]}' trace.log

пример

Давайте рассмотрим этот тестовый файл:

$ cat trace.log
1|2|3|4|jerry|6
a|b|c|d|phil|f
1|2|3|4|jerry|6

Исходная команда производит этот вывод:

$ awk -F "|" '{ print $5 }' trace.log | sort | uniq | xargs -l sh -c 'echo -n $0 && grep "$0" trace.log | wc -c'
jerry32
phil15

Предложенная команда, которая циклически просматривает файл один раз, выдает такой вывод:

$ awk -F "|" '{ a[$5]+=1+length($0) } END{for (name in a) print name,a[name]}' trace.log
jerry 32
phil 15

Как это устроено

  • -F "|"

    Это устанавливает разделитель полей для ввода.

  • a[$5]+=1+length($0)

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

    Длина количества length($0) не включает перевод строки, заканчивающий строку. Следовательно, мы добавляем один к этому, чтобы учесть \n .

  • END{for (name in a) print name,a[name]}

    После того, как мы прочитали файл один раз, мы распечатали суммы.

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