как использовать sed, awk или gawk, чтобы печатать только то, что соответствует?

82

Я вижу много примеров и справочных страниц о том, как делать такие вещи, как поиск и замена, используя sed, awk или gawk.

Но в моем случае у меня есть регулярное выражение, которое я хочу запустить против текстового файла, чтобы извлечь определенное значение. Я не хочу выполнять поиск и замену. Это вызывается из bash. Пусть используется пример:

Пример регулярного выражения:

.*abc([0-9]+)xyz.*

Пример входного файла:

a
b
c
abc12345xyz
a
b
c

Проще, как это звучит, я не могу понять, как правильно вызвать sed/awk/gawk. То, что я надеялся сделать, изнутри bash script имеет:

myvalue=$( sed <...something...> input.txt )

Вещи, которые я пробовал, включают:

sed -e 's/.*([0-9]).*/\\1/g' example.txt # extracts the entire input file
sed -n 's/.*([0-9]).*/\\1/g' example.txt # extracts nothing
  • 8
    Вау ... люди проголосовали за этот вопрос -1? Действительно ли это неуместно в вопросе?
  • 0
    Это кажется совершенно уместным, использование Regex и мощных утилит командной строки, таких как sed / awk или любого другого редактора, такого как vi, emacs или teco, может больше походить на программирование, чем просто на использование какого-то старого приложения. ИМО это относится на ТАК больше, чем на СУ.
Показать ещё 1 комментарий
Теги:
awk
sed
gawk

10 ответов

38
Лучший ответ

My sed (Mac OS X) не работал с +. Я попробовал * вместо этого, и я добавил тег p для печати:

sed -n 's/^.*abc\([0-9]*\)xyz.*$/\1/p' example.txt

Для сопоставления хотя бы одного числового символа без +, я бы использовал:

sed -n 's/^.*abc\([0-9][0-9]*\)xyz.*$/\1/p' example.txt
  • 0
    Спасибо, это сработало и для меня, когда я использовал * вместо +.
  • 2
    ... и параметр "p" для печати совпадения, о котором я тоже не знал. Еще раз спасибо.
Показать ещё 2 комментария
28

Вы можете использовать sed для этого

 sed -rn 's/.*abc([0-9]+)xyz.*/\1/gp'
  • -n не печатать полученную строку
  • -r это делает так, чтобы у вас не было выхода из группы захвата parens ().
  • \1 совпадение группы захвата
  • /g глобальное соответствие
  • /p распечатать результат

Я написал для себя инструмент, который упрощает

rip 'abc(\d+)xyz' '$1'
  • 2
    Это, безусловно, лучший и самый хорошо объясненный ответ на данный момент!
  • 0
    С некоторыми объяснениями лучше понять, что не так с нашей проблемой. Спасибо !
14

Я использую perl, чтобы сделать это проще для себя. например.

perl -ne 'print $1 if /.*abc([0-9]+)xyz.*/'

Это запускает Perl, параметр -n указывает Perl читать в одной строке за раз от STDIN и выполнять код. Параметр -e указывает инструкцию для запуска.

Команда запускает регулярное выражение в строке read, и если оно соответствует, выводит содержимое первого набора треков ($1).

Вы можете сделать это, также будет несколько имен файлов. например.

perl -ne 'print $1 if /.*abc([0-9]+)xyz.*/' example1.txt example2.txt

  • 0
    Спасибо, но у нас нет доступа к Perl, поэтому я спрашивал о sed / awk / gawk.
5

Если ваша версия grep поддерживает его, вы можете использовать параметр -o для печати только части любой строки, соответствующей вашему регулярному выражению.

Если нет, то здесь лучший sed я мог бы придумать:

sed -e '/[0-9]/!d' -e 's/^[^0-9]*//' -e 's/[^0-9]*$//'

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

Проблема с чем-то вроде:

sed -e 's/.*\([0-9]*\).*/&/' 

.... или

sed -e 's/.*\([0-9]*\).*/\1/'

... заключается в том, что sed поддерживает только "жадное" совпадение... так что первый. * будет соответствовать остальной части строки. Если мы не сможем использовать отрицательный класс символов для достижения не-жадного соответствия... или версии sed с Perl-совместимыми или другими расширениями для своих регулярных выражений, мы не сможем получить точное соответствие шаблона с пространством шаблонов (строка).

  • 0
    Вы можете просто объединить две команды sed следующим образом: sed -n 's/[^0-9]*\([0-9]\+\).*/\1/p'
  • 0
    Ранее не знал о опции -o на grep. Приятно знать. Но он печатает весь матч, а не "(...)". Так что если вы соответствуете "abc ([[: digit:]] +) xyz", то вы получите "abc" и "xyz", а также цифры.
2

Вы можете использовать awk с match() для доступа к захваченной группе:

$ awk 'match($0, /abc([0-9]+)xyz/, matches) {print matches[1]}' file
12345

Это пытается сопоставить шаблон abc[0-9]+xyz. Если он делает это, он сохраняет свои срезы в массиве matches, первым элементом которого является блок [0-9]+. Поскольку match() возвращает позицию символа или индекс, где начинается эта подстрока (1, если она начинается в начале строки), она запускает действие print.


С помощью grep вы можете использовать внешний вид и внешний вид:

$ grep -oP '(?<=abc)[0-9]+(?=xyz)' file
12345

$ grep -oP 'abc\K[0-9]+(?=xyz)' file
12345

Это проверяет шаблон [0-9]+, когда он встречается внутри abc и xyz, и просто печатает цифры.

2

perl - самый чистый синтаксис, но если у вас нет perl (не всегда там, я понимаю), тогда единственный способ использовать gawk и компоненты регулярного выражения - использовать функцию gensub.

gawk '/abc[0-9]+xyz/ { print gensub(/.*([0-9]+).*/,"\\1","g"); }' < file

вывод входного файла образца будет

12345

Примечание: gensub заменяет все регулярное выражение (между//), поэтому вам нужно поставить. * до и после ([0-9] +), чтобы избавиться от текста до и после номера в подстановке.

  • 2
    Умное, работоспособное решение, если вам нужно (или хотите) использовать gawk. Вы это заметили, но, чтобы было ясно: awk не из GNU не имеет gensub () и поэтому не поддерживает это.
  • 0
    Ницца! Однако может быть лучше использовать match() для доступа к захваченным группам. Смотрите мой ответ для этого.
1

Если вы хотите выбрать строки, выделите биты, которые вы не хотите:

egrep 'abc[0-9]+xyz' inputFile | sed -e 's/^.*abc//' -e 's/xyz.*$//'

Он в основном выбирает нужные строки с помощью egrep, а затем использует sed, чтобы отключить биты до и после номера.

Вы можете увидеть это в действии здесь:

pax> echo 'a
b
c
abc12345xyz
a
b
c' | egrep 'abc[0-9]+xyz' | sed -e 's/^.*abc//' -e 's/xyz.*$//'
12345
pax> 

Обновление: очевидно, если у вас действительно более сложная ситуация, RE должны будут меня модифицировать. Например, если в начале и конце у вас всегда было одиночное число, закодированное в нуле или более нечисло:

egrep '[^0-9]*[0-9]+[^0-9]*$' inputFile | sed -e 's/^[^0-9]*//' -e 's/[^0-9]*$//'
  • 0
    Интересно ... Так что нет простого способа применить сложное регулярное выражение и получить обратно только то, что находится в разделе (...)? Потому что, хотя я вижу, что вы сделали здесь сначала с grep, а затем с sed, наша реальная ситуация намного сложнее, чем отбрасывание «abc» и «xyz». Регулярное выражение используется, потому что много текста может появиться по обе стороны от текста, который я хотел бы извлечь.
  • 0
    Я уверен, что есть лучший способ, если RE действительно сложны. Возможно, если бы вы предоставили еще несколько примеров или более подробное описание, мы могли бы подстроить наши ответы под себя.
-1

вы можете сделать это с помощью оболочки

while read -r line
do
    case "$line" in
        *abc*[0-9]*xyz* ) 
            t="${line##abc}"
            echo "num is ${t%%xyz}";;
    esac
done <"file"
-2

Для awk. Я бы использовал следующий script:

/.*abc([0-9]+)xyz.*/ {
            print $0;
            next;
            }
            {
            /* default, do nothing */
            }
  • 0
    который получает grep как поведение ...
  • 0
    Это не выводит числовое значение ([0-9+]) , это выводит всю строку.
-2
gawk '/.*abc([0-9]+)xyz.*/' file
  • 2
    Это не похоже на работу. Он печатает всю строку вместо совпадения.
  • 0
    в вашем образце входного файла этот шаблон представляет собой целую строку. право??? если вы знаете, что шаблон будет находиться в определенном поле: используйте $ 1, $ 2 и т. д., например, файл gawk '$ 1 ~ /.*abc([0-9]+)xyz.*/'

Ещё вопросы

Сообщество Overcoder
Наверх
Меню