Ответ 1
sort ip_addresses | uniq -c
Сначала будет напечатан счетчик, но кроме этого он должен быть именно тем, что вы хотите.
Предположим, у вас есть файл, содержащий IP-адреса, по одному адресу в каждой строке:
10.0.10.1
10.0.10.1
10.0.10.3
10.0.10.2
10.0.10.1
Вам нужна оболочка script, которая подсчитывает каждый IP-адрес, сколько раз он появляется в файле. Для предыдущего ввода вам нужен следующий вывод:
10.0.10.1 3
10.0.10.2 1
10.0.10.3 1
Один из способов сделать это:
cat ip_addresses |uniq |while read ip
do
echo -n $ip" "
grep -c $ip ip_addresses
done
Однако это действительно неэффективно.
Как бы вы более эффективно решали эту проблему с помощью bash?
(Одна вещь для добавления: я знаю, что ее можно решить с помощью perl или awk, меня интересует лучшее решение в bash, а не на этих языках.)
ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ:
Предположим, что исходный файл равен 5 ГБ, а машина, работающая с алгоритмом, имеет 4 ГБ. Таким образом, сортировка не является эффективным решением, и не читает файл более одного раза.
Мне понравилось хэш-табличное решение - кто-нибудь может улучшить это решение?
ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ № 2:
Некоторые люди спрашивали, зачем мне это делать в bash, когда это проще, например, Perl. Причина в том, что на машине я должен был сделать это, я не был доступен для меня. Это была специально построенная Linux-машина без большинства инструментов, к которым я привык. И я думаю, что это была интересная проблема.
Итак, пожалуйста, не обвиняйте этот вопрос, просто игнорируйте его, если вам это не нравится.: -)
sort ip_addresses | uniq -c
Сначала будет напечатан счетчик, но кроме этого он должен быть именно тем, что вы хотите.
Быстрый и грязный метод выглядит следующим образом:
cat ip_addresses | sort -n | uniq -c
Если вам нужно использовать значения в bash, вы можете назначить всю команду переменной bash, а затем просмотреть результаты.
PS
Если команда sort опущена, вы не получите правильных результатов, так как uniq смотрит только на последовательные идентичные строки.
Каноническое решение - это тот, который упоминается другим респондентом:
sort | uniq -c
Он короче и более кратким, чем то, что может быть написано на Perl или awk.
Вы пишете, что не хотите использовать сортировку, потому что размер данных больше, чем размер основной памяти машины. Не следует недооценивать качество реализации команды сортировки Unix. Сортировка использовалась для обработки очень больших объемов данных (считайте исходные данные биллинга AT & T) на машинах с 128 КБ (что составляет 131 072 байта) памяти (PDP-11). Когда сортировка встречает больше данных, чем заданный предел (часто настраиваемый близко к размеру основной памяти машины), он сортирует данные, которые он прочитал в основной памяти, и записывает их во временный файл. Затем он повторяет действие со следующими фрагментами данных. Наконец, он выполняет сортировку слияния в этих промежуточных файлах. Это позволяет сортировать данные, которые во много раз больше, чем основная память устройства.
для суммирования нескольких полей на основе группы существующих полей используйте пример ниже: (замените $1, $2, $3, $4 в соответствии с вашими требованиями)
cat file
US|A|1000|2000
US|B|1000|2000
US|C|1000|2000
UK|1|1000|2000
UK|1|1000|2000
UK|1|1000|2000
awk 'BEGIN { FS=OFS=SUBSEP="|"}{arr[$1,$2]+=$3+$4 }END {for (i in arr) print i,arr[i]}' file
US|A|3000
US|B|3000
US|C|3000
UK|1|9000
Кажется, вам нужно либо использовать большой объем кода для имитации хэшей в bash, чтобы получить линейное поведение, либо придерживаться суперлинейных версий quadratic.
Среди этих версий решение saua является лучшим (и самым простым):
sort -n ip_addresses.txt | uniq -c
Я нашел http://unix.derkeiler.com/Newsgroups/comp.unix.shell/2005-11/0118.html. Но это уродливо, как черт...
cat ip_addresses | sort | uniq -c | sort -nr | awk '{print $2 " " $1}'
эта команда даст вам желаемый результат
Вероятно, вы можете использовать файловую систему как хэш-таблицу. Псевдокод следующим образом:
for every entry in the ip address file; do
let addr denote the ip address;
if file "addr" does not exist; then
create file "addr";
write a number "0" in the file;
else
read the number from "addr";
increase the number by 1 and write it back;
fi
done
В конце концов, все, что вам нужно сделать, - это перемещать все файлы и печатать имена и номера файлов в них. В качестве альтернативы вместо хранения счетчика вы можете каждый раз добавлять в файл пробел или новую строку, а в конце - просто посмотреть размер файла в байтах.
Решение (группа с помощью mysql)
grep -ioh "facebook\|xing\|linkedin\|googleplus" access-log.txt | sort | uniq -c | sort -n
Результат
3249 googleplus
4211 linkedin
5212 xing
7928 facebook
Я понимаю, что вы ищете что-то в Bash, но если кто-то может что-то искать в Python, вы можете подумать об этом:
mySet = set()
for line in open("ip_address_file.txt"):
line = line.rstrip()
mySet.add(line)
Как значения в наборе по умолчанию уникальны, а Python довольно хорош в этом, вы можете что-то выиграть. Я не тестировал код, поэтому он может быть прослушан, но это может привести вас туда. И если вы хотите подсчитать случаи, использование dict вместо набора легко реализовать.
Изменить: Я паршивый читатель, поэтому я ответил неправильно. Здесь приведен фрагмент с диктоном, в котором будут учитываться события.
mydict = {}
for line in open("ip_address_file.txt"):
line = line.rstrip()
if line in mydict:
mydict[line] += 1
else:
mydict[line] = 1
Теперь словарь mydict содержит список уникальных IP-ключей и количество раз, которое они имели в качестве своих значений.
Я чувствую, что ассоциативный массив awk также удобен в этом случае
$ awk '{count[$1]++}END{for(j in count) print j,count[j]}' ips.txt
Группа по почте здесь
Я бы сделал это вот так:
perl -e 'while (<>) {chop; $h{$_}++;} for $k (keys %h) {print "$k $h{$k}\n";}' ip_addresses
но uniq может работать для вас.
Большинство других решений подсчитывают дубликаты. Если вам действительно нужно группировать пары ключевых значений, попробуйте следующее:
Вот мои данные примера:
find . | xargs md5sum
fe4ab8e15432161f452e345ff30c68b0 a.txt
30c68b02161e15435ff52e34f4fe4ab8 b.txt
30c68b02161e15435ff52e34f4fe4ab8 c.txt
fe4ab8e15432161f452e345ff30c68b0 d.txt
fe4ab8e15432161f452e345ff30c68b0 e.txt
Это будет печатать пары значений ключа, сгруппированные по контрольной сумме md5.
cat table.txt | awk '{print $1}' | sort | uniq | xargs -i grep {} table.txt
30c68b02161e15435ff52e34f4fe4ab8 b.txt
30c68b02161e15435ff52e34f4fe4ab8 c.txt
fe4ab8e15432161f452e345ff30c68b0 a.txt
fe4ab8e15432161f452e345ff30c68b0 d.txt
fe4ab8e15432161f452e345ff30c68b0 e.txt
Сортировка может быть опущена, если порядок не значим
uniq -c <source_file>
или
echo "$list" | uniq -c
если исходный список является переменной