при использовании сопоставления с образцом в Lua с круглыми скобками, как использовать% 2 для получения группы захвата

Я пытаюсь разобрать текстовый файл и преобразовать его в таблицу (или JSON) с помощью lua. Пример тестового файла выглядит следующим образом:

ipv4     2 tcp      6 3598 ESTABLISHED src=192.168.1.117 dst=137.194.2.78 sport=59078 dport=80 packets=4 bytes=298 src=137.194.2.78 dst=132.227.127.212 sport=80 dport=59078 packets=3 bytes=567 [ASSURED] mark=0 use=2
ipv4     2 udp      17 55 src=192.168.1.117 dst=157.56.149.60 sport=49991 dport=3544 packets=5 bytes=445 [UNREPLIED] src=157.56.149.60 dst=132.227.127.212 sport=3544 dport=49991 packets=0 bytes=0 mark=0 use=2
ipv4     2 tcp      6 3420 ESTABLISHED src=192.168.1.104 dst=193.51.224.187 sport=35918 dport=443 packets=19 bytes=2521 src=193.51.224.187 dst=132.227.127.212 sport=443 dport=35918 packets=16 bytes=9895 [ASSURED] mark=0 use=2
ipv4     2 udp      17 59 src=192.168.1.117 dst=192.168.1.255 sport=17500 dport=17500 packets=139 bytes=23908 [UNREPLIED] src=192.168.1.255 dst=192.168.1.117 sport=17500 dport=17500 packets=0 bytes=0 mark=0 use=2
...

Обратите внимание, что данные в каждой строке можно разделить на две части в зависимости от направления (потоки прямого и обратного пути). Если у вас есть Linux system / openwrt router, вы можете получить аналогичный тестовый файл, используя команду conntrack или прочитав /proc/net/nf_conntrack.

Я хочу получить следующую информацию:

{ 1:
    {
    "bytes":    298,
    "src":      "192.168.1.117",
    "sport":    59078,
    "layer4":   "tcp",
    "dst":      "137.194.2.78",
    "dport":    80,
    "layer3":   "ipv4",
    "packets":  4,
    "rbytes":   567,
    "rpackets": 3
    },
{ 2: ...

где rbytes, rpackets предназначены для байтов и пакетов в обратном направлении (вторая половина строки 1 в моем примере текстового файла).

Мой парсер такой: *

function conntrack(callback)
local connt = {}
if io.open("conntrack.temp", "r") then

    for line in io.lines("conntrack.temp") do
            line = line:match("^(.-( [^ =]+=).-)%2")
        local entry, flags = _parse_mixed_record(line, " +")

            if flags[6] ~= "TIME_WAIT" then
                entry.layer3 = flags[1]
                entry.layer4 = flags[3]
                for i=1, #entry do
                    entry[i] = nil
                end
                if callback then
                    callback(entry)
                else
                    connt[#connt+1] = entry
                end
            end
    end   
else
    return nil
end
return connt
end

function _parse_mixed_record(cnt, delimiter)
delimiter = delimiter or "  "
local data = {}
local flags = {}

for i, l in pairs(cnt:split("\n")) do
    for j, f in pairs(l:split(delimiter)) do
        local k, x, v = f:match('([^%s][^:=]*) *([:=]*) *"*([^\n"]*)"*')
        if k then
            if x == "" then
                table.insert(flags, k)
            else
                data[k] = v
            end
        end
    end
end

return data, flags
end

Вызов вышеуказанной функции (после включения простого split метода в код) я могу проанализировать файл только до первой половины каждой строки. Таким образом, в основном не анализируются ни rbytes, ни rpackets. Я знаю, что за это отвечает код

line = line: match ("^ (.- ([^ =] + =) .-)% 2")

Оператор print(line), следующий за этой строкой кода, показывает мне:

ipv4 2 tcp 6 3598 УСТАНОВЛЕННЫЙ src = 192.168.1.117 dst = 137.194.2.78 sport = 59078 dport = 80 пакетов = 4 байта = 298

Таким образом, оператор разбивает каждую строку файла, используя запутанное сопоставление с образцом, которое я как бы понимаю, немного поигравшись с ним. То, что я до сих пор не понимаю, - это %2, которое появляется после захвата паттерна. Я знаю, что он используется для того, чтобы каким-то образом получить доступ к пойманному шаблону, но как мне изменить этот оператор, чтобы line содержал как байты прямого пути, так и количество пакетов, а также обратный путь? Мой главный вопрос: каков именно шаблон в этом утверждении? Я, вероятно, собираюсь удалить эту строку, чтобы проанализировать весь оператор, но я хотел понять, почему исходные кодировщики делают это.

Я просмотрел руководство по сопоставлению шаблонов lua, но все еще не понимаю, как записывать вывод с помощью %<some_number>. Почему не работают %1 или %3?

Я нашел два соответствующих вопроса о стеке: Q1 , Q2. Было бы желательно более глубокое объяснение.

Кроме того, в настоящее время я не могу восстановить значение тайм-аута (5-е слово в строке 1 3598) или состояние соединения (ESTABLISHED, [ASSURED]) с помощью кода, который я здесь предоставил. Я все еще новичок в lua и надеюсь скоро это исправить.

* ПРИМЕЧАНИЕ. Это моя фиксированная версия синтаксического анализатора, доступного в модуле luci sys на маршрутизаторах openwrt. См. исходный код luci.sys для подробностей.

Во время работы над настройкой отношения 12.09 я заметил, что их net.conntrack () не работает из-за сбоя в синтаксическом анализе объекта в надлежащий формат JSON. Соответствующая функция, использующая этот шаблон, указана в файле sys.lua и называется функцией conntrack (обратный вызов) и внутренней функцией _parse_mixed_record (cnt, разделитель). Мой роутер использовал luci-0.11 и lua 5.1.4.


person Sarthak Grover    schedule 25.07.2013    source источник


Ответы (1)


Этот шаблон был разработан, чтобы сохранить только переднюю часть каждой линии. Вот как это происходит. Вторая скобка ( [^ =]+=) захватывает первую подстроку формы " stuff=". Тогда %2 в конце шаблона будет соответствовать только в том случае, если та же самая строка " stuff=" появится снова. Итак, в строке вроде

ipv4     2 tcp      6 3598 ESTABLISHED src=192.168.1.117 dst=137.194.2.78 sport=59078 dport=80 packets=4 bytes=298 src=137.194.2.78 dst=132.227.127.212 sport=80 dport=59078 packets=3 bytes=567 [ASSURED] mark=0 use=2

второй захват будет " src=", поэтому первый захват, который назначен для line, будет всей начальной частью строки, пока не появится секунда времени src=, то есть это начальное часть:

ipv4     2 tcp      6 3598 ESTABLISHED src=192.168.1.117 dst=137.194.2.78 sport=59078 dport=80 packets=4 bytes=298

Если вы хотите получить и вторую половину и назначить ее другой переменной, вы можете заменить оператор line = ... на

line1, _, line2 = line:match("^(.-( [^ =]+=).-)(%2.*)$")

Это назначит строке1 первую половину строки (как было ранее присвоено строке), а строке2 - оставшуюся часть, начиная со второго появления " src=". В приведенном выше примере строки вы получите

line1 = "ipv4     2 tcp      6 3598 ESTABLISHED src=192.168.1.117 dst=137.194.2.78 sport=59078 dport=80 packets=4 bytes=298"
line2 = " src=137.194.2.78 dst=132.227.127.212 sport=80 dport=59078 packets=3 bytes=567 [ASSURED] mark=0 use=2"

Примечание: _ между line1 и line2 служит для захвата второго захвата (здесь строка " src="), помните, что совпадение возвращает все захваты по порядку, хотите вы их или нет.

person Omar Antolín-Camarena    schedule 25.07.2013