sed: использование переменных в нескольких строках - PullRequest
1 голос
/ 18 мая 2009

Я пытаюсь "привязать" привязку для конкретного пользователя из файла журнала LDAP. Строки, которые мне нужны, будут распределены по нескольким строкам в журнале. Вот пример ввода:

[2009/04/28 17:04:42.414] DoBind on connection 0x7c8affc0
[2009/04/28 17:04:42.414] Bind name:cn=admin,ou=appids,o=admineq, version:3, authentication:simple
[2009/04/28 17:04:42.415] Failed to authenticate local on connection 0x6cc8ee80, err = log account expired (-220)
[2009/04/28 17:04:42.416] Sending operation result 53:"":"NDS error: log account expired (-220)" to connection 0x6cc8ee80
[2009/04/28 17:04:42.416] Operation 0x3:0x60 on connection 0x6cc8ee80 completed in 3 seconds
[2009/04/28 17:04:42.416] Sending operation result 0:"":"" to connection 0x7c8affc0
[2009/04/28 17:04:42.416] Operation 0x1:0x60 on connection 0x7c8affc0 completed in 0 seconds
[2009/04/28 17:04:48.772] DoSearch on connection 0x7c8affc0
[2009/04/28 17:04:48.772] Search request:
base: "o=intranet"
scope:2  dereference:0  sizelimit:0  timelimit:600  attrsonly:0
filter: "(guid='03ADmin)"
attribute: "cn"
attribute: "cn"
attribute: "cn"
attribute: "cn"
attribute: "objectClass"
attribute: "guid"
attribute: "mail"
[2009/04/28 17:04:48.773] Sending operation result 0:"":"" to connection 0x7c8affc0
[2009/04/28 17:04:48.773] Operation 0xe851:0x63 on connection 0x7c8affc0 completed in 0 seconds

Для этого примера результатом должно быть следующее:

[2009/04/28 17:04:42.414] DoBind on connection 0x7c8affc0
[2009/04/28 17:04:42.414] Bind name:cn=admin,ou=appids,o=admineq, version:3, authentication:simple
[2009/04/28 17:04:42.416] Sending operation result 0:"":"" to connection 0x7c8affc0
[2009/04/28 17:04:42.416] Operation 0x1:0x60 on connection 0x7c8affc0 completed in 0 seconds

По сути, это журнал операций сервера по нескольким соединениям. Мне нужно проанализировать время, затрачиваемое на операции «связывания» пользователем-администратором, но этот сервер очень занят, поэтому мне нужно устранить много шума.

В псевдокоде:

for each line in file
    if line contains "DoBind" and next line contains "cn=admin"
        print both lines
        find the connection number X in lines
        skip lines until "Sending operation result.*to connection X" is found
        print two lines

Я хотел бы получить строки «DoBind», которым предшествует пользователь «cn = admin», а затем строки результата, которые перечислены в соответствии с номером соединения «0x7c8affc0» в этом примере. Между началом и концом связывания, которые мне не нужны, могут выполняться другие операции, такие как сообщение «Не удалось аутентифицировать», которое происходит в другом соединении.

Кроме того, после выполнения привязки будут выполняться другие операции с подключением, в которых я не заинтересован. В приведенном выше примере операции DoSearch, происходящие после привязки, не должны регистрироваться.

Я пытаюсь сделать это с помощью «sed», который выглядел как правильный инструмент для работы. Увы, однако, я новичок, и это опыт обучения. Вот что у меня есть:

/.*DoBind on connection \(0x[0-9a-f]*\)\n.*Bind name:cn=OblixAppId.*/ p
/.*Sending operation result.*to connection \1\nOperation.*on connection \1 completed.*/ p

sed жалуется на вторую строку, где я использую '\ 1'. Я пытаюсь захватить адрес подключения и использовать его в последующем поиске, чтобы захватить строки результата, но я, очевидно, неправильно его использую. Переменные '#' кажутся локальными для каждой операции поиска.

Есть ли способ передачи "переменных" из одного поиска в другой, или я должен вместо этого изучать Perl?

Ответы [ 4 ]

2 голосов
/ 19 мая 2009

В качестве интеллектуальной задачи я предложил решение с использованием sed (как было запрошено), но я бы сказал, что использование какой-то другой технологии (perl в моем любимом) было бы проще для понимания и, следовательно, проще для поддержки.

У вас есть несколько вариантов использования многострочной обработки в sed:

  • вы можете использовать пространство для хранения - которое можно использовать для хранения всего или части пространства образца для последующей обработки или
  • Вы можете добавить дополнительные строки в пространство образца , используя такие команды, как N.

    вы можете использовать пробел

Примечание: в приведенном ниже примере используется GNU sed. Кроме того, его можно настроить для работы с Solaris sed, изменив синтаксис с несколькими командами (';' заменен на). Я использовал вариант GNU sed, чтобы сделать скрипт более компактным.

Сценарий ниже прокомментирован для читателя и шахта.

sed -n '
# if we see the line "DoBind" then store the pattern in the hold space
/DoBind/ h

# if we see the line "cn=admin", append the pattern to the holdspace
# and branch to dobind
/cn=admin/{H;b dobind}

# if we see the pattern "Sending...." append the hold space to the
# pattern and  branch to doop
/Sending operation result/{G;b doop}

# branch to the end of the script
b

# we have just seen a cn=admin, ad the hold space contains the last
# two lines
:dobind

# swap hold space with pattern space
x

# print out the pattern space
p

# strip off everying that is not the connection identifier
s/^.*connection //
s/\n.*$//

# put it in the hold space
x

# branch to end of script.
b

# have just seen "Sending operation" and the current stored connection
#identifier has been appended to the pattern space
:doop

# does the connection id on both lines match? Yes do to gotop.
/connection \(0x[0-9a-f]*\).*\n\1$/ b gotop

# branch to end of script
b

# pattern contains two lines "Sending....", and the connection id.
:gotop

# delete the second line
s/\n.*$//

# read the next line and append it to the pattern space.
N

# print it out
p

# clear the pattern space, and put it into the hold space - hence
# clearing the hold space
s/^.*$//
x

'

1 голос
/ 18 мая 2009

Вам захочется внимательно посмотреть на ссылку sed, если вы хотите сделать это за один проход - вы, безусловно, можете это сделать. Посмотрите на команды sed, которые меняют местами буферы удержания и паттернов, и сравните их. Вы можете написать многошаговое правило, которое соответствует «cn = admin», и поменять его в буфере хранения, а затем сопоставить с шаблоном «DoBind», когда буфер хранения не пуст.

Я не могу вспомнить команды от руки, но это не очень сложно; вам просто нужно посмотреть это в справочной документации.

1 голос
/ 18 мая 2009
fgrep -B1 cn=admin logfile | 
sed -n 's/.*DoBind on connection \(.*\)/\1/p' | 
fgrep -wf - logfile

Этот первый fgrep извлекает строку Bind и предыдущую строку (-B1), sed извлекает номер подключения, а последний fgrep находит все строки, содержащие один из номеров подключения.

Это двухпроходное решение, один проход возможен, но более сложен в реализации.

Edit: Вот решение, которое делает то, что вы хотите в Python. Тем не менее, обратите внимание, что это не совсем правильно, так как не будет корректно обрабатывать чередующиеся строки журнала между различными соединениями - я оставлю это на ваше усмотрение, если вы будете достаточно внимательны, чтобы исправить это. Это также немного неэффективно и делает больше регулярных выражений компиляций и соответствий, чем необходимо.

import re

todo = set()
display_next = False
previous_dobind = None

for line in open('logfile'):
  line = line.strip()
  if display_next:
    print line
    display_next = False
    continue
  dobind = re.search('DoBind on connection (.*)', line)
  bind = re.search('Bind name:cn=admin', line)
  oper = re.search('Sending operation result.*to connection (.*)', line)
  if dobind:
    previous_dobind = (dobind.groups(1), line)
  elif previous_dobind:
    if bind:
      todo.add(previous_dobind[0])
      print previous_dobind[1]
      print line
    previous_dobind = None
  elif oper:
    conn = oper.groups(1)
    if conn in todo:
      print line
      display_next = True
      todo.remove(conn)
0 голосов
/ 19 мая 2009

Ну, я не смог найти решение с помощью sed. Вот мое уродливое решение Perl:

open INFILE, $ARGV[0] or die "Couldn't open file $ARGV[0]";
while (<INFILE>) {
  if (/(.*DoBind on connection (0x[0-9a-f]*))/) {
    $potentialmatch = $1; $connid = $2;
    $currentline = <INFILE>;
    if ($currentline =~ /(.*Bind name:cn=OblixAppId.*)/) {
      print $potentialmatch . "\n" . $1 . "\n";
      $offset = tell INFILE;
      while($currentline = <INFILE>) {
        if ($currentline =~ /(.*Sending operation result.*to connection $connid.*)/) {
          print "$1\n";
          next;
        }
        if ($currentline =~ /(.*Operation.*on connection $connid completed.*)/) {
          print  "$1\n";
          seek INFILE, $offset, 0;
          last;
        }
      }
    }
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...