AWK/SED/getline — Как упростить / улучшить этот пример?

#awk #sed #getline

#awk #sed #getline

Вопрос:

Я пытаюсь взять входной файл из 3 столбцов и разделить его на основе условия в столбце 3. Я думаю, вам будет легче показать, чем объяснить:

Входной файл:

 outputfile1.txt
 26         NCC      1     # First Start
 38         NME      2
 44         NSC      1     # Start2
 56         NME      2
 62         NCC      1     # Start3
...
314         NCC      1     # Start17
326         NME      2
332         NSC      1     # Start18
344         NME      2
349         NME      2     # Final End
  

(Хэшированные комментарии не являются частью файла, я добавил, чтобы было понятнее).

Столбец 3 используется для определения новой записи «START»

Значения «НАЧАЛО / КОНЕЦ» взяты из столбца 1

«TITLE» я бы хотел, чтобы все значения из столбца 2 между последовательными «ЗАПУСКАМИ»

Желаемый результат

 outputfile2.txt
START=26 ; END=43 ; TITLE=NCC_NME
START=44 ; END=61 ; TITLE=NSC_NME
START=62 ; END=79 ; TITLE=NCC_...
...
START=314 ; END=331 ; TITLE=NCC_NME
START=332 ; END=349 ; TITLE=NSC_NME
  

Грубый скрипт, который «почти» делает это, но в процессе создает временные файлы с 5 отдельными столбцами.

 awk '{ print $1 }' outputfile1.txt | sed '$d' > tempfile1.txt
awk '{ print $1-1 }' outputfile1.txt | sed '$d' > tempfile2.txt
sed '$d' outputfile1.txt | awk 'NR{print $3-p}{p=$3}' > tempfile3.txt

awk '  { getline value < "tempfile1.txt" }
       { if (NR==1)
       print value ;
       else if( $1 != 1 )
       print value }' tempfile3.txt > tempfile4.txt

awk '  { getline value < "tempfile2.txt" }
       { if (NR==1)
       print value ;
       else if ( $1 != 1 )
       print value }' tempfile3.txt | sed '1d' > tempfile5.txt
awk 'END{print $1}' outputfile1.txt >> tempfile5.txt

awk '   { getline value < "tempfile5.txt" }
        {print "START="$0 " ; END="value}' tempfile4.txt > outputfile2.txt
  

Содержимое временных файлов

        |  temp1     temp2     temp3
NR=1   |  26        25        1
NR=2   |  38        37        1
NR=3   |  44        43        -1
NR=4   |  56        55        1
NR=5   |  62        61        -1
...    |  ...       ...       ...
NR=33  |  314       313       -1
NR=34  |  326       325       1
NR=35  |  332       331       -1
NR=36  |  344       343       1
----------------------------------
       | temp4     temp5
NR=1   |  26        43
NR=2   |  44        61
NR=3   |  62        79
...    |  ...       ...
NR=17  |  314       331
NR=18  |  332       359
  

Текущий вывод

 outputfile2.txt
START=26 ; END=43
START=44 ; END=61
START=62 ; END=79
...
START=314 ; END=331
START=332 ; END=349
  

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

1. Несколько советов на будущее: 1) никогда не используйте sed для обработки нескольких строк ввода одновременно, вместо этого используйте awk. 2) Вам никогда не понадобится sed, если вы уже используете awk. 3) Существует лишь очень мало конкретных типов проблем, для которых подходит решение с использованием getline, см. awk.info/?tip/getline

2. его лучше опубликовать вопрос codereview.stackexchange.com

3. Спасибо за комментарии. Будем иметь в виду!

Ответ №1:

Попробуйте:

 awk '
  function print_range() {
    printf "START=%s ; END=%s ; TITLE=%sn", start, end-1, title
  }

  {
    end=$1
  }

  # if column 3 is equal to 1, then there is a new start
  $3==1 {
    if(title) print_range()
    start=$1
    title=$2
    next
  }

  # if the label in field 2 is not part of the title then add it
  title!~"(^|_)" $2 "(_|$)" {
    title=title"_"$2
  }

  END {
    end  
    print_range()
  }
' file
  

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

1. Спасибо. Очень признателен! 🙂

Ответ №2:

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

 awk '{ 
  if(NR==1){    

     # if we are the first record we initialize our variables
     PREVIOUS_ONE=$1
     TITLE=$2
     PREVIOUS_THIRD=$3

  } else {

    # as long as the new third column is larger we update our variables
    if(PREVIOUS_THIRD < $3) { 

       TITLE=TITLE"_"$2
       PREVIOUS_THIRD=$3

    } else {  
       # this means the third column was smaller
       # we print out the data and reinitialize our variables
       print "START="PREVIOUS_ONE" ; END="$1-1" ; TITLE= "TITLE;

       PREVIOUS_ONE=$1 
       TITLE=$2
       PREVIOUS_THIRD=$3
    }   
  }
  }' outputfile1.txt