как использовать sed, awk или gawk, чтобы печатать только то, что соответствует? - PullRequest
95 голосов
/ 14 ноября 2009

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

42 голосов
/ 14 ноября 2009

Мой 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
32 голосов
/ 03 февраля 2016

Вы можете использовать sed, чтобы сделать это

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

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

rip 'abc(\d+)xyz' '$1'
17 голосов
/ 14 ноября 2009

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

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

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

Инструкция выполняет регулярное выражение для прочитанной строки и, если она совпадает, выводит содержимое первого набора скобок ($1).

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

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

5 голосов
/ 14 ноября 2009

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

3 голосов
/ 22 августа 2016

Вы можете использовать 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 голосов
/ 30 апреля 2013

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

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

выходной файл примера ввода будет

12345

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

1 голос
/ 14 ноября 2009

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

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 голосов
/ 28 ноября 2009

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

while read -r line
do
    case "$line" in
        *abc*[0-9]*xyz* ) 
            t="${line##abc}"
            echo "num is ${t%%xyz}";;
    esac
done <"file"
0 голосов
/ 14 ноября 2009
gawk '/.*abc([0-9]+)xyz.*/' file
0 голосов
/ 14 ноября 2009

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

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