#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