LINUX.ORG.RU

вопрос по Баш и awk как написать алгоритм


0

2

Приветствую коллеги. Есть данные вида:
10:10:2013/11:38:20 127.0.0.1 user1
10:10:2013/10:19:04 127.0.1.1 user1
10:10:2013/03:09:19 127.1.0.224 user1
10:10:2013/03:03:29 127.0.3.210 user1
10:10:2013/09:00:30 127.0.0.2 user2
10:10:2013/08:51:25 127.0.0.3 user2
10:10:2013/08:49:00 127.0.0.4 user3

На данный момент появилась необходимость обработать данные так что бы получилось следующее:
user1
127.0.0.1 10:10:2013/11:38:20
127.0.1.1 10:10:2013/10:19:04
127.1.0.224 10:10:2013/03:09:19
127.0.3.210 10:10:2013/03:03:29

user2
127.0.0.2 10:10:2013/09:00:30
127.0.0.3 10:10:2013/08:51:25

user3
127.0.0.4 10:10:2013/08:49:00

Думаю лучше это делать с помощью AWK, но ни как не пойму как мне написать так, что бы повторяющиеся логины становились едиными и к каждому из низ список IP адресов с ними связанные. Подскажите может кто сталкивался с подобным?



Последнее исправление: l1nuxor (всего исправлений: 3)

Да делайте всё просто и тупо, сначала вырезаете из строк последнее поле, где user, отдаёте его sort | uniq и во временных файл. А потом для каждой строки из этого временного файла делаете grep или sed.

Ну можно, конечно, awk, массивы, но лучше начнайте с простых скриптов, но самостоятельно написаных.

mky ★★★★★
()
Последнее исправление: mky (всего исправлений: 1)

Типа так:

#!/bin/bash

INFILE="test.txt"

for u in $(cat "$INFILE"| cut -d ' ' -f 3 | sort | uniq)
do 
  echo $u
  grep $u "$INFILE" | awk '{print ($2," ",$1)}'
done

melvin
()

Если совсем в лоб и не думая:

#!/bin/sh

INP="${1}"

for i in `sort -k 3 "${INP}" | awk '{print $3}' | uniq`; do
  echo "${i}"
  grep "[[:blank:]]\{1,\}${i}$" "${INP}" | awk '{print $2" "$1}'
  echo
done
ksicom
()

Вот тупой awk вариант с массивами:

awk '{lines[$3] = lines[$3] $2 " " $1 "\n";} END {for (name in lines) { print name; print lines[name]; }}'
io ★★
()

Вот так в лоб:

for i in `cat FILENAME | awk '{print $3}' | sort -u`;do echo $i;cat FILENAME | awk "/$i/{print (\$2" "\$1)}";done

generator ★★★
()

Не знаю awk. Но думаю, что можно сначала получить список пользователей. А потом, использую этот список, структурировать файл. Если бы нужен был питон, то написал бы как-то так:

#!/usr/bin/env python

def f_out():
    with open("lor.txt") as f:
        data = f.readlines()
        return data

logins = [x.split()[2] for x in f_out()]
u_logins = set(logins)

for i in u_logins:
    print i
    for j in f_out():
        if i in j:
            print j.split()[1], j.split()[0]

nanosleep2
()

не надо никаких массивов:

awk '
BEGIN{memo="foo"} 
{ 
    if(memo != $3) {
        memo = $3; 
        print memo
    } 
    print $2, $1
}'

вместо memo=«foo» ставь любой другой placeholder, которого точно не будет среди логинов

BreadFan ★★
()
Последнее исправление: BreadFan (всего исправлений: 1)
Ответ на: комментарий от nanosleep2

А если мы читаем из stdin? Два раза прочитать данные не поучится.

# -*- coding: utf-8 -*-
#!/usr/bin/env python

from collections import defaultdict
from sys import stdin

entries = defaultdict(list)

for line in stdin:
    date, ip, user = line.split()
    entries[user].append((ip, date))

for user in sorted(entries.keys()):
    print user
    for ip, date in entries[user]:
        print ip, date
    print
theNamelessOne ★★★★★
()

если в исходном файле строки сортированы по последнему полю (а по приведённому семплу похоже что так оно и есть), то всё делается в один проход и без лишних массивов

#!/usr/bin/tclsh
set prev ""
while { ! [ catch gets line ] } {
   set data [ lrange $line 0 end-1 ]
   set user [ lindex $line end ]
   if { $user != $prev } {
      puts ""
      puts $user
      set prev $user
   }
   puts $data
}

на tcl-е достаточно наглядно, а на bash думаю сами переведёте

MKuznetsov ★★★★★
()
Ответ на: комментарий от theNamelessOne

⇓ причина появления pep8

collections — неспортивно.

#!/usr/bin/env python3
records = [ line.split() for line in open("testfile", 'r', encoding='utf-8') ]
result = { x[2]: { ' '.join((y[1], y[0])) for y in records if y[2] == x[2]} for x in records }
for x in sorted(result.keys()):
    print("{0}\n{1}".format(x,"\n".join(sorted(result[x]))))
Можно заменить open() на stdin.

anonymous
()

Загоняешь в sqlite3 и делаешь SELECT(ы) какие хочешь пока фантазия не иссякнет.

sdio ★★★★★
()

Речь ведь идёт о BASH, не так ли?

declare -A Logons
while read l; do
 a=($l)
 Logons[${a[2]}]+="${a[1]} ${a[0]}
"
done <logons.txt
for uid in ${!Logons[@]}; do
 echo -e "$uid\n${Logons[$uid]}"
done
Ну вот как бы и всё. И никакого awk здесь даром не нужно.
Хотелось бы заметить, что ОС, в которых нет BASH 4-й версии и трудно таковой поставить - это хуже, чем кусок дерьма. Просто не пользуйтесь ими :)

DRVTiny ★★★★★
()
Ответ на: комментарий от DRVTiny

Кстати, по-моему можно сразу считывать в массив a, минуя переменную l. Секундочку...

#!/bin/bash
declare -A Logons
while read -a a; do
 Logons[${a[2]}]+="${a[1]} ${a[0]}
"
done <logons.txt
for uid in $(echo "${!Logons[@]}" | tr ' ' '\n' | sort); do
 echo -e "$uid\n${Logons[$uid]}"
done

DRVTiny ★★★★★
()
Последнее исправление: DRVTiny (всего исправлений: 1)
Ответ на: комментарий от anonymous

Нигде в требованиях не было сказано о необходимости сортировки результата. Может быть, там воообще нужно выводить логины в той последовательности, в которой они встречались в исходном файле.
Ну и да, Perl использует библиотеки, написанные на Си, компиляция кода на Си использует библиотеки машинного кода для различных архитектур, а этот самый машинный код с точки зрения CPU с архитектурой CISC (по сути почти каждая машинная команда ассемблера - это такая процедура, состоящая из ограниченного набора микрокоманд, которые уже куда ближе к RISC) распадается на микрокоманды, распределяемые по конвейерам... Жизнь вообще сложная штука :)

DRVTiny ★★★★★
()
Последнее исправление: DRVTiny (всего исправлений: 1)
Ответ на: комментарий от sdio

Загоняешь в sqlite3 и делаешь SELECT(ы) какие хочешь пока фантазия не иссякнет.

Берёшь микроскоп и х*рачишь по гвоздям, пока руки не устанут

DRVTiny ★★★★★
()

Если исходные записи не сгруппированы по третьему полю, а вывод должен быть сгруппированным, то так:

sort -k 3 FILENAME | awk '$3 != last { print $3; last = $3 } { print $2, $1 }'
иначе просто
awk '$3 != last { print $3; last = $3 } { print $2, $1 }' FILENAME

unterwulf
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.