sed/awk: заменить числа в строке после последнего появления '.'

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

xfreerdp /sec:rdp /u:fake.user /v:fake.host.com
1
01.02.2020, 17:19
5 ответов

Если номера полей постоянны -, как в полях 3 и 5 вашего вопроса -, попробуйте

awk '
function CHX(FLD)   {n = split ($FLD, T, ".")
                     sub (T[n] "$", sprintf ("%X", T[n]), $FLD)
                    }
    {CHX(3)
     CHX(5)
    }
1
' file
07:36:03.848461 IP 172.17.3.41.814D > 172.17.3.43.4400 UDP, length 44
07:36:03.848463 IP 172.17.3.42.814D > 172.17.3.43.4401 UDP, length 44
07:36:03.848467 IP SYSTEM-A.814D > 172.17.3.43.440A UDP, length 45
07:36:03.848467 IP SYSTEM-B.814D > 172.17.3.43.440B UDP, length 45

Например,. завершающее двоеточие в поле 5:

awk '
function CHX(FLD)       {n = split ($FLD, T, "[^0-9]")
                         TRM = ""
                         if (!T[n])     {n--
                                         TRM = substr ($FLD, length($FLD))
                                        }
                         sub (T[n] TRM "$", sprintf ("%X%s", T[n], TRM), $FLD)
                        }
        {CHX(3)
         CHX(5)
        }
1
' file
0
28.04.2021, 23:24

Сperl:

perl -pe 's/\s\S+\.\K\d+/sprintf "%x", $&/ge' < your-file

Ищет слова, состоящие из пробела (\s), за которым следует последовательность из одного или нескольких(+)не -пробелов (\S), точка и последовательность из одной или нескольких цифр(\d+)и заменяет конечную часть (, начало которой помечено как \K), на тот же ($&), отформатированный в xдесятичном(gглобально, замена eоценивается как perl-код ).

1
28.04.2021, 23:24

С любым awk в любой оболочке на каждой машине UNIX:

$ cat tst.awk
function mkPortHex(fldNr,       port, sfx) {
    port = sfx = $fldNr
    sub(/.*\./,"",port)
    sub(/.*[0-9]/,"",sfx)
    sub(/[^.]+$/,sprintf("%x%s",port,sfx),$fldNr)
}
{
    mkPortHex(3)
    mkPortHex(5)
    print
}

$ awk -f tst.awk file
07:36:03.848461 IP 172.17.3.41.814d > 172.17.3.43.4400: UDP, length 44
07:36:03.848463 IP 172.17.3.42.814d > 172.17.3.43.4401: UDP, length 44
07:36:03.848467 IP SYSTEM-A.814d > 172.17.3.43.440a: UDP, length 45
07:36:03.848467 IP SYSTEM-B.814d > 172.17.3.43.440b: UDP, length 45

С GNU awk для соответствия 3-го аргумента():

$ cat tst.awk
function mkPortHex(fldNr) {
    match($fldNr,/(.*\.)([0-9]+)(:?)/,a)
    $fldNr = a[1] sprintf("%x",a[2]) a[3]
}
{
    mkPortHex(3)
    mkPortHex(5)
    print
}

$ awk -f tst.awk file
07:36:03.848461 IP 172.17.3.41.814d > 172.17.3.43.4400: UDP, length 44
07:36:03.848463 IP 172.17.3.42.814d > 172.17.3.43.4401: UDP, length 44
07:36:03.848467 IP SYSTEM-A.814d > 172.17.3.43.440a: UDP, length 45
07:36:03.848467 IP SYSTEM-B.814d > 172.17.3.43.440b: UDP, length 45
1
28.04.2021, 23:24

Спасибо всем за ответы! Все работают! Однако я хотел бы опубликовать свое решение и здесь. Теперь я знаю, что я специально просил sedи awkдля этого, но мой ввод представляет собой поток tcpdump, и я хотел изменить номера портов на шестнадцатеричные. Поэтому я просмотрел исходный код и изменил следующие строки:

(void)snprintf(buf, sizeof(buf), "%u", i);

-

(void)snprintf(buf, sizeof(buf), "%x", i); // prints hexadecimal

(void)snprintf(buf, sizeof(buf), "%u", i);

с

по

(void)snprintf(buf, sizeof(buf), "%x", i); // prints hexadecimal

Скомпилирован двоичный файл, и теперь tcpdump выводит порты в шестнадцатеричном формате.

0
28.04.2021, 23:24

Сначала следует спросить, разумно ли это, имея 172.17.3.43.440a, где первые 4 поля, разделенные точками, являются десятичными, а 5-е — шестнадцатеричным, выглядит странно. Однако давайте предположим, что у ОП есть веские причины.

ОП предоставил нам некоторые образцы данных и желаемый результат, это очень полезно.

ОП показывает нам echo 33101 | sed -e 's/.*://' | xargs printf "%x\n", что дает нам некоторое представление об их мыслительных процессах. Это говорит мне, что они на ложном пути. Они хотят как-то разбить строку (с помощью awkили sedна основе тегов ), с помощью printfпреобразовать ее в шестнадцатеричный формат, а затем снова собрать линию. Повторите для каждой строки. Хотя очевидно, что это можно заставить работать, это очень медленно. Процессы Unix и Linux дешевы, но не бесплатны. Этот подход будет использовать много процессов на строку, и можно ожидать, что вывод потока tcpdump будет состоять из многих сотен строк. Желательно выполнять изменение, используя только несколько процессов на файл , чтобы это имело разумную скорость, если это вообще возможно.

Итак, о выборе инструмента. Теги предполагают sedи awk. Я бы отверг sed. Онturing complete(предполагает бесконечный объем памяти )и поэтому может делать все, что можно сделать на любом другом языке, но это не значит, что он будет легким или читабельным. Если бы я делал это, я, вероятно, потянулся бы к perlили python, но awkвполне разумно, так что давайте воспользуемся этим.

awkпрограммы перебирают строки сами по себе, так что это хорошее начало. ОП говорит о third column, но, глядя на желаемый результат, они также хотят и пятый. Они не хотят, чтобы миллисекунды в первом столбце преобразовывались в шестнадцатеричный формат, хотя это тоже число after last occurrence of '.'. Так что есть выбор, который нужно сделать,мы перебираем каждый из столбцов (, называемых «полями» в awk )после первого, или мы просто делаем третий и пятый? Либо будет работать. Возьмем общий случай и сделаем все столбцы. Это дает нам

    for(f=2;f<NF;f++){
       # do something with field f
    }

Теперь нам нужно посмотреть, соответствует ли поле ., за которым следует число, а затем необязательный :(, поэтому мы преобразуем 5-е поле в исходные данные ). Это можно сделать с помощью регулярных выражений, одной из мощных идей, популяризированных Unix.

    for(f=2;f<NF;f++){
       if ($f ~ /\.[0-9][0-9]*:?$/) {
           # this field needs to be converted
       }
    }

Теперь займемся преобразованием. Используйте match, чтобы разбить шаблон, и sprintf, чтобы вернуть его, чтобы получить окончательную программу

#!/usr/bin/awk -f
{
   for(f=2;f<NF;f++){
       if ($f ~ /\.[0-9][0-9]*:?$/) {
           match($f,/^(.*\.)([0-9][0-9]*)(:?)$/,a)
           $f=sprintf("%s%x%s",a[1],a[2],a[3])
       }
    }
    print
}
0
28.04.2021, 23:24

Теги

Похожие вопросы