Печатать строки после uniq с условием столбца

У меня есть файл со следующим содержимым в файле

192.168.168.23 pg.something
181.135.56.13 pg.nothing
15.123.96.12 l.everything
15.151.15.3 f.something
15.151.15.3 pg.something
64.196.12.34 pg.nothing
15.123.96.12 l.everything
181.168.56.13 pg.nothing
192.168.168.23 pg.something
192.168.168.23 l.everything
192.12.56.152 l.everything
181.135.56.13 pg.nothing
64.196.12.34 pg.nothing
64.196.12.34 pg.something
181.135.56.13 pg.nothing
64.196.12.34 l.everything

Я пытаюсь выяснить, нет ли обращений по каждому IP-адресу к каждому пользователю, отсортированному по IP-адресу.

Я попробовал это.

for i in `cat test_file |awk '{print $1}'|sort |uniq -c |sort -rn |awk '{print $2}'`; do grep $i test_file;done |uniq -c |awk '{print $2,$3,$1}'

и получил

64.196.12.34 pg.nothing 2
64.196.12.34 pg.something 1
64.196.12.34 l.everything 1
192.168.168.23 pg.something 2
192.168.168.23 l.everything 1
181.135.56.13 pg.nothing 3
15.151.15.3 f.something 1
15.151.15.3 pg.something 1
15.123.96.12 l.everything 2
192.12.56.152 l.everything 1
181.168.56.13 pg.nothing 1

Этот вывод в порядке. Но мне интересно, есть ли способ изменить этот вывод, чтобы он выглядел так...

64.196.12.34 pg.nothing 2
             pg.something 1
             l.everything 1
192.168.168.23 pg.something 2
               l.everything 1
181.135.56.13 pg.nothing 3
15.151.15.3 f.something 1
            pg.something 1
15.123.96.12 l.everything 2
192.12.56.152 l.everything 1
181.168.56.13 pg.nothing 1

То есть удалить только повторяющиеся IP-адреса...

Заранее спасибо.


person Bharath Tupaki    schedule 27.02.2014    source источник


Ответы (3)


Вы можете изменить последнюю команду awk с помощью:

awk '{if ($2!=a) {print $2"\t"$3"\t"$1} else {print "\t\t"$3"\t"$1}}{a=$2}'

Который дает :

64.196.12.34    pg.nothing      2
                pg.something    1
                l.everything    1
192.168.168.23  pg.something    2
                l.everything    1
181.135.56.13   pg.nothing      3
15.151.15.3     f.something     1
                pg.something    1
15.123.96.12    l.everything    2
192.12.56.152   l.everything    1
181.168.56.13   pg.nothing      1
person jrjc    schedule 27.02.2014
comment
Спасибо. Оно работает. Именно то, что мне было нужно. Немного подправил. awk '{if ($2!=a) {print $2\t$3\t$1} else {print \t\t$3\t$1}}{a=$2}'. - person Bharath Tupaki; 27.02.2014
comment
нет пб! Просто примите ответ! (и спасибо за =, я отредактирую вашу версию) - person jrjc; 27.02.2014

Это то, что вычисляется с нуля:

awk '
     {a[$1,$2]++; b[$1]; c[$2]}
     END{for (i in b) {for (j in c) if (a[i,j]) print i,j,a[i,j]}}
    ' file | awk '
                  $1==prev {print FS $2 FS $3; next} {prev=$1; print}
                 '

Первая часть делает подсчет:

$ awk '{a[$1,$2]++; b[$1]; c[$2]} END{for (i in b) {for (j in c) if (a[i,j]) print i,j,a[i,j]}}' a 
192.168.168.23 pg.something 2
192.168.168.23 l.everything 1
192.12.56.152 l.everything 1
64.196.12.34 pg.nothing 2
64.196.12.34 pg.something 1
64.196.12.34 l.everything 1
15.151.15.3 f.something 1
15.151.15.3 pg.something 1
15.123.96.12 l.everything 2
181.135.56.13 pg.nothing 3
181.168.56.13 pg.nothing 1

Объяснение

  • {a[$1,$2]++; b[$1]; c[$2]} отслеживать все комбинации строк: a хранит 1-е и 2-е поля, b — 1-е и c — 2-е.
  • END{for (i in b) {for (j in c) if (a[i,j]) print i,j,a[i,j]}} продолжает перебирать 1-е и 2-е поля, печатая только те, которые совпадают.

И из этого он делает группировку:

$ awk '{a[$1,$2]++; b[$1]; c[$2]} END{for (i in b) {for (j in c) if (a[i,j]) print i,j,a[i,j]}}' a | awk '$1==prev {print FS $2 FS $3; next} {prev=$1; print}'
192.168.168.23 pg.something 2
 l.everything 1
192.12.56.152 l.everything 1
64.196.12.34 pg.nothing 2
 pg.something 1
 l.everything 1
15.151.15.3 f.something 1
 pg.something 1
15.123.96.12 l.everything 2
181.135.56.13 pg.nothing 3
181.168.56.13 pg.nothing 1

Объяснение

  • '$1==prev {print FS $2 FS $3; next} в случае, если предыдущая строка имела такое же 1-е поле, печатать только из 2-го поля.
  • {prev=$1; print}' в противном случае печатать в обычном режиме.
person fedorqui 'SO stop harming'    schedule 27.02.2014

Вот решение версии Perl:

#!/usr/bin/perl

use warnings;
use strict;

my %data;

while (<DATA>) {
    chomp;
    my ($ip, $dom) = split;
    $data{$ip}->{$dom}++;
}

while(my ($ip, $doms) = each %data) {
    print "$ip\t";
    my ($dom, $cnt) = each %$doms;
    print "$dom $cnt\n";
    while (($dom, $cnt) = each %$doms) {
        print "\t\t$dom $cnt\n";
    }
    print "\n";
}

__DATA__
192.168.168.23 pg.something
181.135.56.13 pg.nothing
15.123.96.12 l.everything
15.151.15.3 f.something
15.151.15.3 pg.something
64.196.12.34 pg.nothing
15.123.96.12 l.everything
181.168.56.13 pg.nothing
192.168.168.23 pg.something
192.168.168.23 l.everything
192.12.56.152 l.everything
181.135.56.13 pg.nothing
64.196.12.34 pg.nothing
64.196.12.34 pg.something
181.135.56.13 pg.nothing
64.196.12.34 l.everything

И его результат:

192.12.56.152   l.everything 1

15.151.15.3     pg.something 1
                f.something 1

64.196.12.34    pg.nothing 2
                pg.something 1
                l.everything 1

181.168.56.13   pg.nothing 1

15.123.96.12    l.everything 2

192.168.168.23  pg.something 2
                l.everything 1

181.135.56.13   pg.nothing 3

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

А вот и адаптированная версия:

while(my ($ip, $doms) = each %data) {
    print "$ip ";
    my ($dom, $cnt) = each %$doms;
    print "$dom $cnt\n";
    my $prefix = ' ' x (length $ip);
    while (($dom, $cnt) = each %$doms) {
        print "$prefix $dom $cnt\n";
    }
}
person Lee Duhem    schedule 27.02.2014