Как сделать так, чтобы uniq рассматривал только первое поле?
Я использую FreeBSD 3.2-RELEASE
Если у меня есть какой-то отсортированный текст, как это last
выход-
zikla13:Oct:20:22:34
zikla13:Oct:5:00:31
zikla14:Oct:17:22:01
zikla14:Oct:12:23:35
zikla14:Oct:12:23:34
zikla14:Oct:12:00:11
zikla14:Oct:11:23:52
zikla14:Oct:5:22:22
zilka13:Oct:13:23:48
zilka13:Oct:11:00:28
zilka13:Oct:9:22:40
Есть ли способ получить uniq -c
рассмотреть только первое поле (может быть, с -s
)? В этом случае вывод должен быть таким:
2 zikla13:Oct:20:22:34
6 zikla14:Oct:17:22:01
3 zilka13:Oct:13:23:48
Или каким-то другим способом, используя awk
?
2 ответа
С GNU uniq, который поддерживает -w
опция:
$ cat data
zikla13:Oct:20:22:34
zikla13:Oct:5:00:31
zikla14:Oct:17:22:01
zikla14:Oct:12:23:35
zikla14:Oct:12:23:34
zikla14:Oct:12:00:11
zikla14:Oct:11:23:52
zikla14:Oct:5:22:22
zilka13:Oct:13:23:48
zilka13:Oct:11:00:28
zilka13:Oct:9:22:40
$ uniq -c -w7 data
2 zikla13:Oct:20:22:34
6 zikla14:Oct:17:22:01
3 zilka13:Oct:13:23:48
Как отмечено в комментариях, предполагается, что первое поле всегда состоит из семи символов, что в вашем примере, но если это не в реальной жизни, я не думаю, что есть способ сделать это с помощью Uniq (плюс, если вы не не имеет GNU Uniq, даже -w
не будет работать), так что вот решение Perl:
$ perl -ne '/(.*?):(.*)/;unless (exists $x{$1}){$x{$1}=[0,$2];push @x, $1};$x{$1}[0]++;END{printf("%8d %s:%s\n",$x{$_}[0],$_,$x{$_}[1]) foreach @x}' <data
2 zikla13:Oct:20:22:34
6 zikla14:Oct:17:22:01
3 zilka13:Oct:13:23:48
Вот как это работает:
$ perl -ne
Запустите perl, не печатая каждую строку по умолчанию, и используйте следующий аргумент в качестве сценария.
/(.*?):(.*)/
Разбить строку ввода на материал перед первым двоеточием и материал после первого двоеточия в $1
а также $2
, split
будет работать здесь также.
unless (exists $x{$1}){$x{$1}=[0,$2];push @x, $1}
Хеш %x
будет использоваться для унификации строк и массива @x
держать их в порядке (вы могли бы просто использовать sort keys %x
, но это предполагает Perl sort
будет сортироваться так же, как сортируются входные данные.) Поэтому, если мы никогда не видели текущий "ключ" (материал перед первым двоеточием), инициализируйте запись хеша для ключа и нажмите клавишу на @x
, Запись хеша для каждого ключа представляет собой двухэлементный массив, содержащий количество и первое значение, увиденное после двоеточия, поэтому выходные данные могут содержать это значение.
$x{$1}[0]++
Увеличьте счет.
END{
Начать блок, который будет запущен после прочтения всех вводимых данных.
printf("%8d %s:%s\n",$x{$_}[0],$_,$x{$_}[1])
Напечатайте счетчик, дополненный пробелами, пробелом, "ключом", двоеточием и прочим после двоеточия.
foreach @x}
Сделайте это для каждого увиденного ключа по порядку и завершите блок END.
<data
Прочитайте из файла с именем data в текущем каталоге, чтобы получить ввод. Вы также можете просто передать в perl, если у вас есть другая команда или конвейер, производящий данные.
Я бы использовал awk
, Отфильтруйте и посчитайте первое разделенное двоеточиями поле, когда оно изменится или мы нажмем EOF, напечатаем всю ранее сохраненную строку и посчитаем:
awk -F: '!seen[$1]++ { line[$1]=$0; if(prev){printf "%d\t%s\n",seen[prev],line[prev]}; prev=$1} END {if(prev){printf "%d\t%s\n",seen[prev],line[prev]}}' data
awk
Скрипт можно развернуть так:
# Count the occurrences of the first field. If first time then...
!seen[$1]++ {
# save the line
line[$1]=$0;
# maybe print the previous line
if (prev) {
printf "%d\t%s\n", seen[prev], line[prev]
};
prev=$1
}
# End of file, so print any previous line we have got saved
END {
if (prev) {
printf "%d\t%s\n", seen[prev], line[prev]
}
}
Если вы можете изменить данные, предоставленные в awk, добавив завершающую пустую строку, вы можете обойтись без всего END {...}
блок, упрощая awk
код и удаление дублирования:
( cat data; echo ) | awk ...