sed — извлекает строку между первым появлением MATCH1 и следующим появлением MATCH2

#regex #bash #sed

#регулярное выражение #bash #sed

Вопрос:

Используя sed , я хотел бы извлечь строку между первым вхождением MATCH1 и следующим вхождением MATCH2 .

echo "abcd MATCH1 STRING MATCH2 efgh MATCH1 ijk MATCH2 MATCH2 lmnop MATCH1" | sed...

Я пробовал это различными способами, но, учитывая, что MATCH1 и MATCH2 оба могут появляться несколько раз подряд, извлечь строку оказалось сложно. Есть идеи, как я могу добиться этого результата?

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

1. Я обновил свой ответ, включив оптимизированную версию единственного вызова sed решения, для которого требуется всего 3 замены вместо 4

Ответ №1:

Они возвращают только строку между совпадениями и работают, даже если MATCH1 == MATCH2 .

 echo ... | grep -Po '^.*?K(?<=MATCH1).*?(?=MATCH2)'
  

Вот sed решение:

 echo ... | sed  's/MATCH1/amp;n/;s/.*n//;s/MATCH2/namp;/;s/n.*//'
  

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

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

1. Это возвращает не строку, а «abcd MATCH1 СТРОКА MATCH2».

2. @lecodesportif: Смотрите мой отредактированный ответ. K Приводит к тому, что .*? не включается в выходные данные grep команды. sed Версия использует метод «разделяй и властвуй» в одном вызове sed .

3. Это возвращает «СТРОКУ MATCH1, СОВПАДАЮЩУЮ с MATCH2», что делает получение строки тривиальным. Но это не сработает, если MATCH1=MATCH2. Решение также должно было бы охватить этот случай.

4. @Dennis Если я правильно понимаю (а может быть, и нет), он хочет просто сопоставить «STRING», а не «MATCH1 STRING MATCH2».

5. @lecodesportif: Смотрите мой отредактированный ответ. Команды теперь возвращают только строку между совпадениями (включая разделяющие пробелы — которые можно легко исключить) и будут работать, если MATCH1 == MATCH2 .

Ответ №2:

Вы можете сделать это с помощью perl, используя не жадные совпадения регулярных выражений:

 echo "abcd MATCH1 STRING MATCH2 efgh MATCH1 ijk MATCH2 MATCH2 lmnop MATCH1" | perl -pe 's|^.*?MATCH1(.*?)MATCH2.*$|1|'
  

sed не поддерживает это.

РЕДАКТИРОВАТЬ: Вот решение, которое сочетает в себе решение Денниса с sed:

 echo "abcd MATCH1 STRING MATCH2 efgh MATCH1 ijk MATCH2 MATCH2 lmnop MATCH1" | grep -Po '^.*?MATCH1.*?MATCH2' | sed 's/^.*MATCH1(.*)MATCH2$/1/'
  

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

1. Мне нужно сделать это с помощью sed или любой встроенной оболочки. Как насчет использования sed для удаления всего, что было до первого появления MATCH1, а затем удаления всего после первого появления MATCH2?

2. Смотрите мой отредактированный ответ. grep -P можно сделать это с помощью K без необходимости sed .

3. эта работа для меня! grep -P не работает с die K (у меня версия 2.10)

Ответ №3:

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

 sed 's/ /n/g' | sed '1,/MATCH1/d;/MATCH2/,$d'
  

Редактировать

Если первой строкой (после подстановки) окажется MATCH1 , gnu sed может обойти это, используя 0,/MATCH1/ вместо 1,/MATCH1/ вот так:

 sed 's/ /n/g' | sed '0,/MATCH1/d;/MATCH2/,$d'
  

Правка2

Оптимизированная версия единственного вызова sed решения, для которого требуется всего 3 замены вместо 4

 sed -r 's/MATCH1/amp;n/;s/MATCH2/namp;/;s/^.*n(.*)n.*$/1/'
  

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

1. Лучше, чем все, что я мог придумать, но, похоже, не работает для строк, начинающихся с «MATCH1».

2. К сожалению, оптимизированная версия не работает, если MATCH1 == MATCH2 .

Ответ №4:

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

 echo "abcd MATCH1 STRING MATCH2 efgh MATCH1 ijk MATCH2 MATCH2 lmnop MATCH1" | 
sed 's/MATCH1/namp;/;s/[^n]*n//;s/(MATCH2).*/1/'
MATCH1 STRING MATCH2