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

  • Две команды sed в одной команде
  • Использование sed для исключения шаблона букв / цифр
  • Заменить выбранные символы
  • Вставьте одинарные кавычки в текстовый файл для использования в качестве SQL-запроса, который следует за open-parens, используя sed
  • Как можно добавить текст после существующего отступа?
  • Sed удаляет все начальные совпадения шаблонов из строки
  • Как заменить строку знаком доллара в sed?
  • 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 
    Linux и Unix - лучшая ОС в мире.