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


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

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

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

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

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

a
b
c
abc12345xyz
a
b
c

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

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
10 91

10 ответов:

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

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

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

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

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

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

я написал инструмент для себя, что делает это проще!--9-->

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

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

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

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

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

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

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

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

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

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

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

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

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

.... или

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

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

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

$ awk 'match(, /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 и просто печатает цифры.

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

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

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

12345

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

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

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> 

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

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

вы можете сделать это с оболочкой

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

по awk. Я бы использовал следующий скрипт:

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