Печатать только поля совпадающего заголовка, начиная с совпадающих записей

Я пытаюсь извлечь определенные поля из моего файла. По сути, поля вывода содержат только совпадающее выражение, причем вывод начинается после совпадающих записей.

Это пример моего ввода. Иногда поля находятся в другом порядке, а также имеют разное количество строк перед заголовком, который я пытаюсь сопоставить.

Мне было трудно понять, как добиться этого с помощью команд cut и sed, и я не мог найти метод awk.

CGATS.17
FORMAT_VERSION  1
KEYWORD "SampleID"
KEYWORD "SAMPLE_NAME"
NUMBER_OF_FIELDS    45
WEIGHTING_FUNCTION "ILLUMINANT, D50"
WEIGHTING_FUNCTION "OBSERVER, 2 degree"
BEGIN_DATA_FORMAT
SampleID    SAMPLE_NAME CMYK_C  CMYK_M  CMYK_Y  CMYK_K  LAB_L   LAB_A   LAB_B   nm380   nm390   nm400
END_DATA_FORMAT
NUMBER_OF_SETS  182
BEGIN_DATA
1   1   40  40  40  0   62.5    6.98    4.09    0.195213    0.205916    0.212827
2   2   0   40  40  0   73.69   25.48   24.89   0.200109    0.211081    0.218222
3   3   40  40  0   0   63.95   12.14   -20.91  0.346069    0.365042    0.377148
4   4   0   70  70  0   58.91   47.69   35.54   0.080033    0.084421    0.087317
END_DATA

Это грязный код, который я использовал, который в основном выполнял свою работу, но без условного поиска в заголовке поля. Команда awk просто удаляет пустые строки, окружающие вывод.

cut -f 7-9 -s input.txt | 
sed -E 's/(LAB_.)//g' |
awk 'NF' > file.txt

Результат, который я ожидал бы, выглядит следующим образом. Он по-прежнему разделен табуляцией и содержит только значения полей, начинающихся непосредственно под (LAB_.)

62.5    6.98    4.09
73.69   25.48   24.89
63.95   12.14   -20.91
58.91   47.69   35.54

person jeffrbauer    schedule 21.01.2012    source источник
comment
+1, прекрасно организованный и отформатированный вопрос! Удачи.   -  person shellter    schedule 21.01.2012


Ответы (3)


Скрипт:

#!/usr/bin/awk -f

# We look for line starting with BEGIN_DATA_FORMAT do the getline function and 
# store location of fields that have "LAB" in their name on the next line.

/^BEGIN_DATA_FORMAT/{
        getline
            for (i=1;i<=NF;i++) 
                    if ($i~/LAB/) a[i]=$i
                } 

# In this regex range we look for lines that have more than 2 fields. For those 
# lines we loop thru each field and see if the location matches to the ones 
# captured in our earlier array (i.e location number of fields that have "LAB" 
# in their name). If we find a match we print those fields. 

/^BEGIN_DATA$/,/^END_DATA$/{
             s="";
             if (NF<2) next; else 
                for (j in a)
            s=s?s"\t"$j:$j
            print s; 
                 }

Контрольная работа:

[jaypal:~/Temp] ./script.awk file
62.5    6.98    4.09    
73.69   25.48   24.89   
63.95   12.14   -20.91  
58.91   47.69   35.54   
person jaypal singh    schedule 21.01.2012
comment
спасибо за мгновенный ответ! Он работает с входными данными с укороченными полями, которые я опубликовал, но не работал с большим файлом, который у меня был. Я понял, что это потому, что мои строки заканчиваются CRLF. Хотя на НЧ работает! В результатах также удален интервал табуляции, а также одна пустая строка вверху и две строки с пробелами внизу. - person jeffrbauer; 21.01.2012
comment
Вы можете изменить его в соответствии с вашим файлом. В цикле for поиграйтесь со значениями. Обратите внимание на столбец, с которого вы хотите начать захват, и последний столбец, который вы хотите. Затем вы можете поместить эти значения в цикл for. - person jaypal singh; 21.01.2012
comment
Вы можете использовать утилиту dos2unix для удаления CRLF. Для пустых строк я могу дать вам обновленный ответ через несколько часов. Не держите под рукой раковину! - person jaypal singh; 21.01.2012
comment
Для пустых строк попробуйте это /^BEGIN_DATA$/,/^END_DATA$/ && NF {for..., если это не сработает, я получу вам рабочее решение, когда я вернусь домой. - person jaypal singh; 21.01.2012
comment
Спасибо за помощь. Я не уверен, что описываю это наилучшим образом, но я пытаюсь найти метод, в котором мне не нужно устанавливать фактический номер поля. Мой опыт работы с синтаксисом в вашем цикле совершенно новый, поэтому, возможно, вы говорите, что я могу поиграть с ним, сопоставив (LAB_.) - person jeffrbauer; 21.01.2012
comment
@JeffBauer Я обновил ответ. Дайте мне знать, как это происходит. - person jaypal singh; 21.01.2012
comment
@JaypalSingh хорошее решение. вторая часть (вложенный цикл) может иметь место для оптимизации.. например. с одной петлей. так как внешний цикл на самом деле ничего не делает. - person Kent; 22.01.2012
comment
Большое спасибо @Kent. Я внес изменения в соответствии с вашими предложениями, и вы правы, использование функции getline намного лучше для захвата местоположения. - person jaypal singh; 22.01.2012
comment
Спасибо, эта новая версия успешно дает результат, который я искал. Мне было интересно, как изменить часть printf, чтобы сделать так, чтобы после последнего поля значений не было вкладки. Будет ли условный оператор хорошим способом сделать это? - person jeffrbauer; 24.01.2012
comment
@JeffBauer Пожалуйста. Да, conditional statements было бы неплохо сделать это. Однако в таких случаях ternary operators пригодится. Они короткие и делают то же самое. Я изменил решение, чтобы использовать ternary operator, как показал Кент. Это удалит дополнительные tab после последнего набора значений. - person jaypal singh; 24.01.2012
comment
Спасибо, это работает как сон. Я пытался понять s в тернарном операторе, который написал Кент, но я подумал, что он похож на тот оператор, который мне нужен. Теперь мне просто нужно лучше познакомиться с переменными в awk. Еще раз спасибо. - person jeffrbauer; 24.01.2012
comment
@JeffBauer Потрясающе!! Добро пожаловать. Я рад, что могу быть полезен. :) - person jaypal singh; 24.01.2012

другой awk-скрипт:

 awk '/^BEGIN_DATA_FORMAT/{getline;f=NF;for(i=1;i<=NF;i++)if($i~/^LAB_[LAB]/)l[i]++;} 
/^BEGIN_DATA/,/^END_DATA/ && NF==f{s=""; for(x in l)s=s?s"\t"$x:$x; print s;}' input

вывод вашего примера ввода:

62.5    6.98    4.09
73.69   25.48   24.89
63.95   12.14   -20.91
58.91   47.69   35.54

некоторые примечания к скрипту awk выше:

  • Обработка заголовков аналогична решению @JayPal, но с небольшим отличием: вы упомянули, что порядок столбцов может быть другим, поэтому для сопоставления заголовков мой скрипт awk искал следующую строку «BEGIN_DATA_FORMAT». поскольку 1-й столбец строки заголовка может быть чем-то другим, кроме SampleID.

  • в выводе, как вы и ожидали, печатаются только значения (разделенные [tab]), но не заголовок. если вы сказали, что порядок столбцов может быть переменным, вы можете потерять информацию заголовка. скажем, какой столбец LAB_L, который A? и т. д. это можно легко сделать, если это действительно необходимо.

person Kent    schedule 21.01.2012
comment
Еще раз спасибо @Kent за ваш отзыв. +1 к этому ответу. - person jaypal singh; 22.01.2012
comment
QQ @Kent: Не могли бы вы объяснить мне ту часть, где вы использовали тернарный оператор s=s?s"\t"$x:$x; print s;? Это намного лучше, чем тот, который я использовал в своем решении, используя printf, а затем print "" для перехода на новую строку. - person jaypal singh; 22.01.2012
comment
@JaypalSingh сначала создаст строку (строки), а затем распечатает ее с помощью оператора печати '\ n'. s=s?s\t$x:$x означает, что если s не пусто, объединяет '\t' между значениями ($x); если s пусто, $x будет первым значением, поэтому нам не нужен начальный '\t'. начальное значение s пусто (). Итак, первое тестирование? было бы ложным. - person Kent; 23.01.2012

Это может сработать для вас:

 sed '/^BEGIN_DATA\>/,/^END_DATA\>/{//d;s/\(\S*\s*\)\{6\}\(\S*\s*\S*\s*\S*\).*/\2/p};d' file

Или остаться с cut:

cut -f7-9 file | sed '/^\([-.0-9]*\s*[-.0-9]*\s*[-.0-9.]*$\)/!d'

Или (но я предполагаю здесь формат вашего входного файла):

sed 's/\s*$//' file | cut -f7-9 | sed '/^BEGIN_DATA$/,/^END_DATA$/{//d;p};d'
person potong    schedule 21.01.2012