Я вижу много примеров и справочных страниц о том, как делать такие вещи, как поиск и замена, используя 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
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
Вы можете использовать sed для этого
sed -rn 's/.*abc([0-9]+)xyz.*/\1/gp'
-n
не печатать полученную строку-r
это делает так, чтобы у вас не было выхода из группы захвата parens ()
.\1
совпадение группы захвата/g
глобальное соответствие/p
распечатать результатЯ написал для себя инструмент, который упрощает
rip 'abc(\d+)xyz' '$1'
Я использую 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
Если ваша версия 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-совместимыми или другими расширениями для своих регулярных выражений, мы не сможем получить точное соответствие шаблона с пространством шаблонов (строка).
sed
следующим образом: sed -n 's/[^0-9]*\([0-9]\+\).*/\1/p'
Вы можете использовать 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
, и просто печатает цифры.
perl - самый чистый синтаксис, но если у вас нет perl (не всегда там, я понимаю), тогда единственный способ использовать gawk и компоненты регулярного выражения - использовать функцию gensub.
gawk '/abc[0-9]+xyz/ { print gensub(/.*([0-9]+).*/,"\\1","g"); }' < file
вывод входного файла образца будет
12345
Примечание: gensub заменяет все регулярное выражение (между//), поэтому вам нужно поставить. * до и после ([0-9] +), чтобы избавиться от текста до и после номера в подстановке.
match()
для доступа к захваченным группам. Смотрите мой ответ для этого.
Если вы хотите выбрать строки, выделите биты, которые вы не хотите:
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]*$//'
вы можете сделать это с помощью оболочки
while read -r line
do
case "$line" in
*abc*[0-9]*xyz* )
t="${line##abc}"
echo "num is ${t%%xyz}";;
esac
done <"file"
Для awk. Я бы использовал следующий script:
/.*abc([0-9]+)xyz.*/ {
print $0;
next;
}
{
/* default, do nothing */
}
([0-9+])
, это выводит всю строку.
gawk '/.*abc([0-9]+)xyz.*/' file