2009-11-14 7 views
87

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

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

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

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

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

a 
b 
c 
abc12345xyz 
a 
b 
c 

Как просто, как это звучит, я не могу понять, как позвонить СЕПГ/AWK/простак правильно , То, что я надеялся сделать, это из моей Баш скрипт есть:

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 
+5

Wow ... люди проголосовали этот вопрос до -1? Неужели это неуместно? –

+0

Кажется вполне уместным, используя Regex и мощные утилиты командной строки, такие как sed/awk или любой редактор, например vi, emacs или teco, могут больше походить на программирование, чем просто использовать какое-то приложение ol. ИМО это принадлежит на SO больше, чем SU. – Dereleased

+0

Возможно, он был отклонен, потому что в его первоначальной форме он четко не определил некоторые из его требований. Это все еще не так, если вы не читаете комментарии OP к ответам (включая тот, который я удалил, когда дела шли грушевидно). – pavium

ответ

38

Мой 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 
+0

Спасибо, это сработало для меня, как только я использовал * вместо +. –

+2

... и параметр «p» для печати матча, о котором я и не знал. Еще раз спасибо. –

+2

Мне пришлось скрыться от '+', а затем он работал для меня: 'sed -n 's /^.* abc \ ([0-9] \ + \) xyz. * $/\ 1/p'' –

15

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

+0

Спасибо, но у нас нет доступа к perl, поэтому я спрашивал о sed/awk/gawk. –

1

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

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> 

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

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

Интересно ... Итак, нет простого способа применить сложное регулярное выражение и вернуться только к тому, что находится в разделе (...)? Причина, когда я вижу, что вы сделали здесь сначала с grep, а затем с sed, наша реальная ситуация намного сложнее, чем сбросить «abc» и «xyz». Регулярное выражение используется, потому что с любой стороны текста, который я хотел бы извлечь, может появляться множество разных текстов. –

+0

Я уверен, что есть * лучший способ, если RE действительно сложны. Возможно, если бы вы предоставили несколько примеров или более подробное описание, мы могли бы подгонять наши ответы. – paxdiablo

-3

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

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

, который получает поведение, подобное grep ... – dmckee

+0

Это не выводит числовое значение '([0-9 +])', это выводит всю строку. –

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

Это не работает. Он печатает всю строку вместо соответствия. –

+0

в вашем примере входного файла, этот шаблон представляет собой целую строку. правильно??? если вы знаете, что шаблон будет находиться в определенном поле: используйте $ 1, $ 2 и т. д. Например, gawk '$ 1 ~ /.*abc([0-9]+)xyz.*/'file – ghostdog74

5

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

+0

Вы можете просто объединить две команды 'sed' таким образом:' sed -n' s/[^ 0-9] * \ ([0-9] \ + \). */\ 1/p'' –

+0

Раньше не знал о опции -o на grep. Приятно знать. Но он печатает весь матч, а не «(...)». Поэтому, если вы соответствуете «abc ([[: digit:]] +) xyz», тогда вы получаете «abc» и «xyz», а также цифры. –

-1

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

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

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

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

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

12345 

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

+2

Умное, работоспособное решение, если вам нужно (или хотите) использовать gawk. Вы отметили это, но чтобы быть ясным: не-GNU awk не имеет gensub(), и поэтому не поддерживает это. – cincodenada

+0

Ницца! Однако лучше всего использовать 'match()' для доступа к захваченным группам. См. [Мой ответ] (http://stackoverflow.com/a/39075261/1983854). – fedorqui

28

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

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

Я написал tool для себя, что делает это проще

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

Это, безусловно, лучший, и наиболее хорошо объясненный ответ до сих пор! –

+0

С некоторым объяснением, лучше понять, что не так с нашей проблемой. Спасибо ! – r4phG

3

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