Извлечь 1 IP-адрес из 2 или более в строке текста

У меня есть около 30000 журналов доступа Apache, некоторые из которых содержат несколько IP-адресов клиентов. Это является результатом того, что Apache регистрирует заголовок X-Forwarded-For вместо IP-адреса клиента. Причина в том, что мы недавно добавили haproxy перед веб-серверами.

В дальнейшем мы будем использовать rpaf для Apache для регистрации только 1 IP-адреса, т. Е. Адреса входящего соединения с haproxy, поэтому это не будет постоянной проблемой.

Что подводит меня к актуальному вопросу:

Как я могу обработать существующие журналы с несколькими IP-адресами, чтобы извлечь только тот, который я хочу. Я предполагаю, что мне нужен Sed или что-то подобное, но я скорее парень из Windows, так что не уверен на 100%.

Правила таковы:

  • Если есть только 1 IP, линия не изменяется.
  • Если есть 2 или более IP-адресов, я хочу сохранить только второй IP-адрес. Они разделены запятыми.

Пример 1, 1 IP

Ввод: 10.1.1.1 - - [29/Jan/2010:11:00:00] .... (остаток строки журнала)

Выходные данные: 10.1.1.1 - - [29/Jan/2010:11:00:00] .... (остаток строки журнала)

Пример 2, 2 IP

Ввод: 10.1.1.1, 10.2.2.2 - - [29 / Jan / 2010: 11: 00: 00].... (остаток строки журнала)

Выходные данные: 10.1.1.1 - - [29 / Jan / 2010: 11: 00: 00].... (остаток строки журнала)

Пример 3, 3 IP

Ввод: 10.1.1.1, 10.2.2.2, 10.3.3.3 - - [29 / Jan / 2010: 11: 00: 00].... (остаток строки журнала)

Выходные данные: 10.2.2.2 - - [29 / Jan / 2010: 11: 00: 00].... (остаток строки журнала)

2 ответа

Решение

Этого можно достичь, выполнив команду sed в ваших журналах:

sed -i "s/^\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+, \)*\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\), [0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+ - -/\2 - -/"

Некоторые объяснения:

  • Общий формат s/MATCH PATTERN/REPLACE PATTERN/
  • Соответствие выполняется для строки "некоторый IP" (от 0 до много раз), за которой следует "некоторый IP" (это тот, который мы хотим сохранить) и, наконец, "некоторый IP - - " (последний IP-адрес, который следует отбросить)
  • Нет необходимости соответствовать первому формату строки (только один IP), так как он не нуждается в изменении.
  • Последний раздел содержит \2 что относится ко второй части матча в скобках.
  • При запуске в оболочке многие символы должны быть экранированы (с обратной косой чертой:), например, в скобках: ( а также )плюс: + (что означает "хотя бы один раз") и период буквального символа: . (иначе это считается подстановочным знаком)
  • -i Опция sed означает изменение файлов на месте. Убедитесь, что вы работаете с копиями!

"Это заставляет мои глаза кровоточить почти так же, как Perl, но это работает".

use strict;
use warnings;
use Regexp::Common qw /net/;

my $ip;
my $restOfLine;
my @ips;    

while (<>) {
    if (/- -.*/) {
        $restOfLine = $&;
    }
    unless (@ips = /($RE{net}{IPv4})/g) {
        print;
        next;
    }
    if ($ips[1]) {
        $ip = splice(@ips,-2,1);
    } else {
        $ip = $ips[0];
    }
    print "$ip " . $restOfLine . "\n";
}

Заставляет мои глаза кровоточить меньше, но, возможно, это только я:-)

Другие вопросы по тегам