3

Я использую 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 ответа2

1

С 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 , но это предполагает, что sort perl будет сортироваться так же, как сортируется ввод.) Поэтому, если мы никогда не видели текущий "ключ" (материал перед первым двоеточием), инициализируйте запись хеша для ключа и нажмите клавишу на @x . Запись хеша для каждого ключа представляет собой двухэлементный массив, содержащий количество и первое значение, увиденное после двоеточия, поэтому выходные данные могут содержать это значение.

$x{$1}[0]++

Увеличьте счет.

END{

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

printf("%8d %s:%s\n",$x{$_}[0],$_,$x{$_}[1])

Напечатайте счет, дополненный пробелами, пробелом, "ключом", двоеточием и прочим после двоеточия.

foreach @x}

Сделайте это для каждого увиденного ключа по порядку и завершите блок END.

<data

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

0

Я бы использовал 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 ...

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