Если строка под предыдущей строкой равна определенному значению первого столбца, выведите две строки

#bash #awk #sed

#bash #awk #sed

Вопрос:

У меня есть переменный входной файл, который можно отформатировать, как показано ниже.

 text1 valueA valueN valueB
text2 valueX
text1 valueC valueN valueD
text2 valueX
text1 valueE valueM valueF
text1 valueG valueM valueH
text1 valueI valueN valueJ
text2 valueX
text1 valueK valueO valueL
text1 valueP valueO valueQ
text1 valueR valueN valueS
text1 valueT valueM valueU
  

Я хочу напечатать только, text1 valueA valueN valueB если text2 valueX существует под предыдущей строкой. Например, вывод должен быть:

 text1 valueA valueN valueB
text2 valueX
text1 valueC valueN valueD
text2 valueX
text1 valueI valueN valueJ
text2 valueX
  

Мне также нужно иметь возможность сопоставлять часть valueX . Допустим valueX=a.b.c.d-e , мне нужно сопоставить a.b.

 myvariable=a.b.
echo $myvariable
a.b.
  

Обновить:
Приносим извинения за нечеткие входные данные… Я предполагал, что смогу сопоставить часть текста, но я ошибся.

Итак, если данные выглядели следующим образом:

 text1 valueA valueN valueB
text2 a.b.c.d-e
text1 valueC valueN valueD
text2 a.b.c.d-e
text1 valueE valueM valueF
text1 valueG valueM valueH
text1 valueI valueN valueJ
text2 a.b.c.d-e
text1 valueK valueO valueL
text1 valueP valueO valueQ
text1 valueR valueN valueS
text1 valueT valueM valueU
  

Как бы вы сопоставили, например, a.b. , если val=a.b.

 echo $val
a.b.
  

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

1. чтобы соответствовать части valueX , например: val Пожалуйста, приведите пример (поскольку val это часть каждой записи).

2. Это было глупо с моей стороны: D … итак, если valueX есть a.b.c.d-e , как вы, например, сопоставляете это как a.b. ?

3.Если я awk '$0~/text2 val/{print p ORS $0}{p=$0}' file получу вывод: text1 valueA valueN valueB text2 valueX и т.д… Но если val=val awk '$0~/text2 $val/{print p ORS $0}{p=$0}' file ничего не возвращает — @JamesBrown

4. Переменные в awk используются не так, вам нужно ввести их с помощью, например, awk -v var=val 'BEGIN{print var}' , так что в этом случае это было бы awk -v s=val '$0~"text2 " s {THE REST} .

5. @JamesBrown да, спасибо за разъяснение по переменным awk. Я прокомментировал ваш пост, используя awk var.

Ответ №1:

Вот одна в awk:

 $ awk '$0=="text2 valueX"{print p ORS $0}{p=$0}' file
  

Вывод:

 text1 valueA valueN valueB
text2 valueX
text1 valueC valueN valueD
text2 valueX
text1 valueI valueN valueJ
text2 valueX
  

Объяснено:

 $ awk '
$0=="text2 valueX" {  # if record is a match
    print p ORS $0    # print previous buffered record and current
}
{
    p=$0              # buffer record for next round
}' file
  

Обновленное обновление с обновленными данными:

Поскольку . это метасимвол регулярного выражения, его необходимо экранировать при вводе, чтобы избежать сопоставления, например, с abbb :

 $ awk -v s="a\.b\." '$0~s{print p ORS $0}{p=$0}' file
  

Выводите сейчас:

 text1 valueA valueN valueB
text2 a.b.c.d-e
text1 valueC valueN valueD
text2 a.b.c.d-e
text1 valueI valueN valueJ
text2 a.b.c.d-e
  

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

1. Обновлено с частичным совпадением.

2. Что я сделал, так это заменил . на _ cat file | sed 's/./_/g' > file2 . Затем я устанавливаю var2=a_b_ . Теперь awk -v s="$var2" '$0~s{print p ORS $0}{p=$0}' file2 выдает хороший вывод с подчеркиванием, но я могу снова заменить его на точки с помощью sed. Вероятно, есть лучший способ сделать это, но, эй, это работает!

3. Пример вывода: text1 valueA valueN valueB text2 a_b_c_d-e

Ответ №2:

Попробуйте это:

 awk 'NR>1amp;amp; $0 == "text2 valueX"{print a"n"$0} {a=$0}' input.txt
  

Обратите внимание, что этот скрипт выводит что-либо, только если текущая строка точно соответствует ‘text2 ValueX’.

Ответ №3:

Другой простой подход с tac и awk .

 tac Input_file | awk '$0=="text2 valueX"{print;getline;print}' | tac
  

Результат будет следующим.

 text1 valueA valueN valueB
text2 valueX
text1 valueC valueN valueD
text2 valueX
text1 valueI valueN valueJ
text2 valueX
  

Некоторые strace из tac (обработки произвольного файла):

 lseek(3, 351051776, SEEK_SET)           = 351051776
read(3, "83,10.1579,56.1257,1412067900n41"..., 8192) = 8192
write(1, "104,210,84,194,10.1313,56.1528,1"..., 4096) = 4096
write(1, "2092,56.1724,1412068200n129,20,3"..., 4096) = 4096
lseek(3, 351043584, SEEK_SET)           = 351043584
read(3, "7900n148,159,77,186,97,10.2090,5"..., 8192) = 8192
write(1, ",140,182,10.1208,56.1784,1412067"..., 4096) = 4096
write(1, "10.1859,56.1239,1412067900n53,17"..., 4096) = 4096
lseek(3, 351035392, SEEK_SET)           = 351035392
  

Вы можете увидеть позицию уменьшения в lseek .

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

1. 1. Узнал, что есть такая вещь, как tac , случайно противоположная cat . Выглядит аккуратно, но меня беспокоит только эффективность использования памяти. Я надеюсь, что tac это не загрузит весь файл в память, чтобы отменить его.

2. @darksky Нет, tac не соответствует. Согласно strace он считывает блоки размером 8 КБ с диска, записывает 2 блока размером 4 КБ (во всяком случае, при настройке моего диска) и lseeks более раннюю позицию в файле.

3. @JamesBrown, Спасибо, Джеймс сэр, за добавление информации о tac

4. @RavinderSingh13 Могу ли я добавить немного strace дампа к вашему ответу?

5. @JamesBrown, вам не нужно спрашивать, сэр, всегда рады добавить / отредактировать мой ответ, сэр, пожалуйста, продолжайте;)

Ответ №4:

sed Решение:

 $ sed -n -e '/text2 valueX/{' -e 'H;x;p;}' -e 'h' file
text1 valueA valueN valueB
text2 valueX
text1 valueC valueN valueD
text2 valueX
text1 valueI valueN valueJ
text2 valueX
  

Измените регулярное выражение на /^text2 valueX$/ , если вы хотите, чтобы совпадала целая строка.

Это равно значению GNU sed:

  sed -n '/text2 valueX/{H;x;p;};h' file
  

POSIX sed должен начинать перевод строки или нового -e блока после { открывающей или } закрывающей скобки.

 H   Append the contents of pattern space to hold space, separate them by a newline character.
x   Exchange the contents of pattern space and hold space.
p   Print the contents of pattern space.
h   Overwrite the contents of hold space with pattern space.
  

/text2 valueX/ это регулярное выражение, оно соответствует текущей строке (пространство шаблонов), если оно соответствует, команда или {} блок после нее будут выполнены.
Внутри блока добавьте строку, чтобы сохранить пробел, в котором уже есть последняя строка. Затем замените пространство удержания пространством шаблона, затем распечатайте.
И после блока переместите текущую строку в пространство удержания, чтобы заменить ее прежнее содержимое.

Примечание: с помощью этого решения у вас не может быть нескольких text2 valueX последовательных строк, иначе будут напечатаны дополнительные строки. Если такое произойдет, пожалуйста, прокомментируйте, дайте мне знать, как вы хотите с ними справиться.

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

1. Спасибо за это! Не могли бы вы, пожалуйста, объяснить {' -e 'H;x;p;}' -e 'h' часть?

2. @TempoChocolate добавил некоторое объяснение.

3. Спасибо за объяснение. Как вы сопоставляете часть valueX , например: val ?

4. @TempoChocolate Что вы хотите для части , которую вы сопоставили? Для разных целей будут использоваться разные способы. Я предлагаю вам ознакомиться с руководством sed .

Ответ №5:

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

 sed '$!N;/ntext2 valueX/p;D' file
  

Откройте движущееся окно из двух строк по всей длине файла.

Если вторая строка совпадает text2 valueX , выведите обе строки.

Удалите первую строку и повторите.

Для измененного вопроса:

 sed '$!N;/ntext2.*a.b./p;D' file
  

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

Примечание. Для нового шаблона может потребоваться, чтобы метасимволы были заключены в кавычки, например, периоду должна предшествовать .

 regexp='a.b.'
sed '$!N;/ntext2.*'"$regexp"'/p;D' file