Использование awk в большом текстовом формате для извлечения определенных символов полей

#string #if-statement #text #awk #substring

#строка #if-оператор #текст #awk #подстрока

Вопрос:

У меня есть большой текстовый файл («,» в качестве разделителя) с некоторыми данными и строкой:

 2014:04:29:00:00:58:GMT: subject=BMRA.BM.T_GRIFW-1.FPN, message={SD=2014:04:29:00:00:00:GMT,SP=5,NP=3,TS=2014:04:29:01:00:00:GMT,VP=4.0,TS=2014:04:29:01:29:00:GMT,VP=4.0,TS=2014:04:29:01:30:00:GMT,VP=3.0}
2014:04:29:00:00:59:GMT: subject=BMRA.BM.T_GRIFW-2.FPN, message={SD=2014:04:29:00:00:00:GMT,SP=5,NP=2,TS=2014:04:29:01:00:00:GMT,VP=3.0,TS=2014:04:29:01:30:00:GMT,VP=3.0}
  

Я хотел бы найти строки, содержащие ‘T_GRIFW’, а затем распечатать поле в размере 1 доллара США начиная с ‘subject’ и далее, и только времена и значения с плавающей запятой начиная с 2 долларов США. Кроме того, я хочу включить оператор if, чтобы, если поле $ 4 == ‘NP= 3’, только поля $5,$6,$9,$10 печатаются после предыдущих полей, и если $ 4 == ‘NP= 2’, печатаются все последующие поля (только с временным значением и плавающими значениями)

Например, результатом двух выборочных строк будет:

 subject=BMRA.BM.T_GRIFW-1.FPN,2014:04:29:00:00:00,5,3,2014:04:29:01:00:00,4.0,2014:04:29:01:30:00,3.0
subject=BMRA.BM.T_GRIFW-2.FPN,2014:04:29:00:00:00,5,2,2014:04:29:01:00:00,3.0,2014:04:29:01:30:00,3.0
  

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

 awk 'BEGIN {FS=","}{OFS=","} /T_GRIFW-1.FPN/ {print $1}' tib_messages.2014-04-29
  

МИЛЛИОН БЛАГОДАРНОСТЕЙ!

Комментарии:

1. Пока у вас не так много. Старайтесь усерднее!

2. Ваш вывод не имеет смысла, поскольку он не соответствует вашему условию. Вы сказали, что если NP=3 , то только поля $5,$6,$9,$10 должно печататься.

3. Привет, Ooga, извините за двусмысленность, я имел в виду после, только $5,$6,$9,$10 должно печататься.

Ответ №1:

Вот исполняемый файл awk, который создаст желаемый результат:

 #!/usr/bin/awk -f

# use a more complicated FS => field numbers counted differently
BEGIN { FS="=|,"; OFS="," }

$2 ~ /T_GRIFW/ amp;amp; $8=="NP" {
    str="subject=" $2 OFS

    # strip ":GMT" from dates and "}" from everywhere
    gsub( /:GMT|[}]/, "")

    # append common fields to str with OFS
    for(i=5;i<=13;i =2) str=str $i OFS

    # print the remaining fields and line separator
    if($9==3) { print str $19, $21 }
    else if($9==2) { print str $15, $17 }
}
  

Помещение этого в файл с именем awko и последующая его модификация awko data приводит к:

 subject=BMRA.BM.T_GRIFW-1.FPN,2014:04:29:00:00:00,5,3,2014:04:29:01:00:00,4.0,2014:04:29:01:30:00,3.0
subject=BMRA.BM.T_GRIFW-2.FPN,2014:04:29:00:00:00,5,2,2014:04:29:01:00:00,3.0,2014:04:29:01:30:00,3.0
  

Я разместил комментарии в сценарии, но вот некоторые вещи, которые можно было бы изложить лучше:

  • Использование более сложного FS означает, что вам не нужно повторно обрабатывать = данные поля
  • Я «обманул» и просто жестко запрограммировал subject (который теперь находится в конце $1 ) для str
  • :GMT и } оказались единственными данными, которые необходимо было принудительно удалить
  • При этом FS даты и числа отделены друг от друга, но все еще доступны для цикла
  • В любом последнем print вызове str уже заканчивается на OFS , поэтому запятую между ним и следующим полем можно пропустить

Комментарии:

1. Я очень рад, что некоторые люди здесь все еще проявляют большое терпение к некоторым из нас, которые изо всех сил пытаются освоить веревки! Большое спасибо n0741337 и S. Ahn!

Ответ №2:

Если я понимаю ваши требования, будет работать следующее:

 BEGIN {
    FS=","
    OFS=","
}

/T_GRIFW/ {
    split($1, subject, " ")
    result = subject[2] OFS
    delete arr
    counter = 1
    for (i = 2; i <= NF; i  ) {
        add = 0
        if ($4 == "NP=3") {
            if (i == 5 || i == 6 || i == 9 || i == 10) {
                add = 1
            }
        }
        else if ($4 == "NP=2") {
            add = 1
        }

        if (add) {
            counter = counter   1
            split($i, field, "=")
            if (match(field[2], "[0-9]*.[0-9] |GMT")) {
                arr[counter] = field[2]
            }
        }
    }

    for (i in arr) {
        gsub(/{|}/,"", arr[i]) # remove curly braces
        result = result arr[i] OFS
    }
    print substr(result, 0, length(result)-1)
}
  

Комментарии:

1. Большое вам спасибо, С. Ан!!