Как использовать sed, awk или gawk для печати только того, что соответствует?

Я вижу много примеров и справочных страниц о том, как делать такие вещи, как поиск и замена, используя 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

Ответ 1

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

Ответ 2

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

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

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

rip 'abc(\d+)xyz' '$1'

Ответ 3

Я использую 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

Ответ 4

Если ваша версия 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-совместимыми или другими расширениями для своих регулярных выражений, мы не сможем получить точное соответствие шаблона с пространством шаблонов (строка).

Ответ 5

Вы можете использовать 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, и просто печатает цифры.

Ответ 6

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

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

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

12345

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

Ответ 7

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

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]*$//'

Ответ 8

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

Поскольку OP требует извлечения группы из паттерна, использование grep -o потребует 2 прохода. Но я все еще нахожу это наиболее интуитивным способом выполнения работы.

$ cat > example.txt <<TXT
a
b
c
abc12345xyz
a
abc23451xyz asdf abc34512xyz
c
TXT

$ cat example.txt | grep -oE 'abc([0-9]+)xyz'
abc12345xyz
abc23451xyz
abc34512xyz

$ cat example.txt | grep -oE 'abc([0-9]+)xyz' | grep -oE '[0-9]+'
12345
23451
34512

Поскольку процессорное время в основном бесплатное, а удобочитаемость для человека бесценна, я склонен реорганизовать свой код, основываясь на вопросе: "Через год, что я собираюсь думать, что это делает?" Фактически, для кода, которым я собираюсь поделиться публично или со своей командой, я даже открою man grep, чтобы выяснить, что такое длинные варианты, и заменить их. Вот так: grep --only-matching --extended-regexp

Ответ 9

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

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

Ответ 10

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

/.*abc([0-9]+)xyz.*/ {
            print $0;
            next;
            }
            {
            /* default, do nothing */
            }

Ответ 11

gawk '/.*abc([0-9]+)xyz.*/' file