Sed для печати только первого соответствия шаблону линии

У меня есть некоторые данные вроде

<td><a href="data1">abc</a> ... <a href="data2">abc</a> ... <a href="data3">abc</a> 

(Будет ссылаться на приведенную выше строку как data в коде ниже)

Мне нужны data1 между первым " и " поэтому я делаю

 echo 'data' | sed 's/.*"\(.*\)".*/\1/' 

но он возвращает мне последнюю строку между " и " всегда, то есть в этом случае она вернет мне data3 вместо data1

Чтобы получить data1 , я в итоге

 echo 'data' | sed 's/.*"\(.*\)".*".*".*".*".*/\1/' 

Как получить data1 без такой избыточности в sed

5 Solutions collect form web for “Sed для печати только первого соответствия шаблону линии”

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

Так как разделитель здесь только один символ, мы можем использовать группу перевернутых скобок, чтобы соответствовать чему-либо, кроме цитаты, т. Е. [^"] , А затем повторяется, чтобы соответствовать нескольким символам, которые не являются кавычками.

 $ echo '... "foo" ... "bar" ...' | sed 's/[^"]*"\([^"]*\)".*/\1/' foo 

Другой способ – просто удалить все до первой цитаты, а затем удалить все, начиная с (новой) первой цитаты:

 $ echo '... "foo" ... "bar" ...' | sed 's/^[^"]*"//; s/".*$//' foo 

В регулярных выражениях Perl спецификаторы * и + можно сделать неживыми, добавив знак вопроса, так что .*? будет что угодно, но как можно меньше символов / байтов.

Я не буду утомлять вас классическим предупреждением против использования простых регулярных выражений для анализа HTML. Достаточно сказать, что вместо этого вы должны использовать выделенный парсер. Тем не менее, проблема здесь в том, что sed использует жадное сопоставление. Таким образом, он всегда будет соответствовать самой длинной строке. Это означает, что ваш .* Продолжается вечно и соответствует всей строке.

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

 $ perl -pe 's/.*?"(.*?)".*/$1/' file data1 

Так как sed не поддерживает не-жадные совпадения, вам нужно другое обмануть. Самым простым было бы использовать подход «не кавычек» в ответе иккачу . Вот альтернатива:

 $ rev file | sed 's/.*"\(.*\)".*/\1/' | rev data1 

Это просто отменяет файл ( rev ), использует ваш оригинальный подход, который теперь работает, поскольку первое вхождение теперь является последним, а затем снова возвращает файл обратно.

Вот несколько способов вытащить данные1 с вашего ввода:

 grep -oP '^[^"]*"\K[^"]*' sed -ne ' /\n/!{y/"/\n/;D;} P ' perl -lne '/"([^"]*)"/ and print($1),last' 

В то время как Question не помечен awk , но почему бы не использовать его, поскольку это просто так:

 awk -F\" '{print $2}' infile.txt 

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

 cat data | grep -Po '(?<=href=").*?(?=")' | head -n1 
  • Сед работает с двумя линиями
  • sed конвертировать 4 пробела в 2
  • Sed: расширение строки чисел до определенного количества цифр путем заполнения нулями
  • Используйте команду sed для поиска текстового файла для записей 10000 и под
  • sed в формате csv
  • Может ли sed заменить новые символы линии?
  • sed удалить начало строки с #, но не с #! (shell-скрипты)
  • Sed - Как я могу вставить строку после определенной строки И какой-то символ?
  • Confused by sed output при использовании N. Кто-нибудь может объяснить эти результаты?
  • sed - выполнить несколько подстановок на строке, найденной путем поиска по шаблону?
  • sed не работает из файла sh, но работает из командной строки
  • sed: получите 2 строки от одного
  • Linux и Unix - лучшая ОС в мире.