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

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

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

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

.*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 ответов


мой sed (Mac OS X) не работал с +. Я пытался!--4--> вместо этого я добавил 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,-n опция инструктирует Perl читать по одной строке за раз из STDIN и выполнять код. The -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 вы можно использовать look-behind и look-ahead:

$ 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