Не нужна вся строка, только совпадение с регулярным выражением

15

Мне просто нужно получить соответствие из регулярного выражения:

$ cat myfile.txt | SOMETHING_HERE "/(\w).+/"

Выход должен быть только то, что было найдено внутри скобки.

Не думайте, что я могу использовать grep, потому что он соответствует всей строке.

Пожалуйста, дайте мне знать, как это сделать.

Алекс Л
источник

Ответы:

12

2 вещи:

  • Как утверждает @Rory, вам нужна -oопция, поэтому печатается только совпадение (вместо целой строки)
  • Кроме того, у вас есть -Pвозможность использовать регулярные выражения Perl, которые включают полезные элементы, такие как « Взгляд вперед» (?= ) и « Взгляд назад» (?<= ) , которые ищут части, но на самом деле не совпадают и не распечатывают их.

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

grep -oP '(?<=\/\()\w(?=\).+\/)' myfile.txt

если файл содержит строчку /(a)5667/, grep выведет «a», потому что:

  • /(найдены \/\(, но, поскольку они находятся в поиске, (?<= ) они не сообщаются
  • aсопоставляется \wи таким образом печатается (из-за -o)
  • )5667/найдены b < \).+\/, но поскольку они находятся в прогнозном режиме, о (?= ) них не сообщается
Dryak
источник
18

Используйте -oопцию в grep.

Например:

$ echo "foobarbaz" | grep -o 'b[aeiou]r'
bar
Рори
источник
4
Боже мой ... Ты хоть представляешь, сколько раз я боролся с sedобратными ссылками, чтобы сделать это?
Insyte
10
Опция o для grep / egrep возвращает только то, что соответствует всему регулярному выражению, а не только то, что в (), как он просил.
Кайл Брандт
1
Тем не менее, это очень хорошая вещь, чтобы знать в любом случае :-)
Кайл Брандт
2
@KyleBrandt: Чтобы соответствовать только одной части (например, скобки), можно пометить остальные с заглядыванием вперед или оглядываясь назад: (? <=) И (? =)
DrYak
6
    sed -n "s/^.*\(captureThis\).*$/\1/p"

-n      don't print lines
s       substitute
^.*     matches anything before the captureThis 
\( \)   capture everything between and assign it to \1 
.*$     matches anything after the captureThis 
\1      replace everything with captureThis 
p       print it
Джошуа
источник
4

Если вы хотите только то, что находится в скобках, вам нужно что-то, что поддерживает захват подстрок (именованные или нумерованные группы захвата). Я не думаю, что grep или egrep могут это сделать, perl и sed могут. Например, с помощью Perl:

Если в файле с именем foo есть строка, которая выглядит следующим образом:

/adsdds      /

И вы делаете:

perl -nle 'print $1 if /\/(\w).+\//' foo

Письмо возвращается. Это может быть не то, что вы хотите, хотя. Если вы сообщите нам, что вы пытаетесь сопоставить, вам может помочь помощь. $ 1 - это то, что было зафиксировано в первой скобке. $ 2 будет второй сет и т. Д.

Кайл Брандт
источник
Я просто пытался сопоставить то, что в скобках. Похоже, что передача его на Perl или PHP-скрипт может быть ответом.
Алекс Л
4

Поскольку вы пометили свой вопрос как bash в дополнение к shell , кроме grep есть еще одно решение :

Начиная с версии 3.0, Bash имеет собственный механизм регулярных выражений, использующий =~оператор, как и Perl.

Теперь, учитывая следующий код:

#!/bin/bash
DATA="test <Lane>8</Lane>"

if [[ "$DATA" =~ \<Lane\>([[:digit:]]+)\<\/Lane\> ]]; then
        echo $BASH_REMATCH
        echo ${BASH_REMATCH[1]}
fi
  • Обратите внимание, что вы должны вызывать его как, bashа не только shдля того, чтобы получить все расширения.
  • $BASH_REMATCH даст всю строку в соответствии с целым регулярным выражением, так <Lane>8</Lane>
  • ${BASH_REMATCH[1]} даст часть, соответствующую 1-й группе, таким образом, только 8
Dryak
источник
Уважаемый @DrYak, я надеюсь, что вы здесь не анализируете XML с помощью регулярных выражений .. :)
joonas.fi
Это еще хуже. Я анализирую ужасную смесь данных XML и FASTA (которые оба используют >символ для совершенно разных целей), как это было продемонстрировано программным обеспечением быстрого масштабирования SANSparallel . Конечно, оба формата чересстрочной развёрнуты без какого-либо выхода. Таким образом, невозможно добавить какую-то стандартную библиотеку XML. И я использую регулярное выражение Bash на этом этапе кода, потому что мне нужно только извлечь пару данных, и 2 регулярные выражения делают работу намного лучше для меня, чем написание специального анализатора для этого беспорядка. #LifeInBioinformatics
DrYak
Другими словами: есть момент, когда извлечь одно единственное число проще с регулярным выражением, чем танцевать целое XML-танго
DrYak
Ха, понял! :)
joonas.fi
2

Предполагая, что файл содержит:

$ cat file
Text-here>xyz</more text

И вы хотите, чтобы символ (ы) между >и</ , вы можете использовать либо:

grep -oP '.*\K(?<=>)\w+(?=<\/)' file
sed -nE 's:^.*>(\w+)</.*$:\1:p' file
awk '{print(gensub("^.*>(\\w+)</.*$","\\1","g"))}' file
perl -nle 'print $1 if />(\w+)<\//' file

Все напечатает строку "xyz".

Если вы хотите захватить цифры этой строки:

$ cat file
Text-<here>1234</text>-ends

grep -oP '.*\K(?<=>)[0-9]+(?=<\/)' file
sed -E 's:^.*>([0-9]+)</.*$:\1:' file
awk '{print(gensub(".*>([0-9]+)</.*","\\1","g"))}' file
perl -nle 'print $1 if />([0-9]+)<\//' file

Стрела
источник
Для меня решающим было понять, что \ d не работает с sed. Есть причина, по которой вы используете [0-9] + там. :)
user27432
@ user27423 Это не делает, но классы POSIX символов ( болезненное чтение , приятное чтение ) делать: echo 'Text-<here>1234</text>-ends' | sed -E 's|.*>([[:digit:]]+)<.*|\1|'. В некоторых случаях (например, [0-9]против [[:digit:]]) они не помогают разборчивости, в других я думаю, что они делают (например, [ \t\n\r\f\v]против [:space:]).
Самуэль Хармер
0

Это выполнит то, что вы просите, но я не думаю, что это то, чего вы действительно хотите. Я поставил .*перед регулярным выражением, чтобы съесть что-нибудь перед матчем, но это жадная операция, так что это соответствует только предпоследнему\w символу в строке.

Обратите внимание, что вам нужно избежать Parens и +.

sed 's/.*\(\w\).\+/\1/' myfile.txt
Чад Хунейкутт
источник