Не потрібен цілий рядок, а лише відповідність регулярному вираженню


16

Мені просто потрібно отримати відповідність із регулярного виразу:

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

Вихід повинен бути лише тим, що було узгоджено, усередині дужок.

Не думаю, що я можу використовувати grep, оскільки він відповідає всій лінії.

Будь ласка, дайте мені знати, як це зробити.

Відповіді:


13

2 речі:

  • Як зазначає @Rory, вам потрібна -oопція, тому друкується лише відповідність (замість цілого рядка)
  • Крім того, ви знаєте -Pможливість використовувати регулярні вирази Perl, які включають корисні елементи, такі як Подивися вперед (?= ) і Подивись позаду (?<= ) , ті шукають частини, але насправді не відповідають і друкують їх.

Якщо ви хочете, щоб тільки частина всередині паренсіса відповідала:

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

якщо файл містить жало /(a)5667/, grep надрукує "a", тому що:

  • /(їх знайдено \/\(, але оскільки вони знаходяться в огляді, про (?<= ) них не повідомляється
  • aвідповідає \wі таким чином друкується (через -o)
  • )5667/знаходяться b < \).+\/, але оскільки вони знаходяться в перспективі, про (?= ) них не повідомляється

18

Використовуйте -oопцію в grep.

Наприклад:

$ echo "foobarbaz" | grep -o 'b[aeiou]r'
bar

4
Добре горе ... Чи маєте ви уявлення, скільки разів я боровся із sedзворотними перевагами, щоб це зробити?
Insyte

10
Опція grep / egrep повертає лише те, що відповідає всьому регулярному виразу, а не лише те, що є в (), як він просив.
Кайл Брандт

1
Однак це дуже добре знати все одно :-)
Кайл Брандт

2
@KyleBrandt: Щоб співставити лише одну частину (наприклад, батьків), можна позначити решту поглядом вперед або озирнутися назад: (? <=) Та (? =)
DrYak

7
    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, крім оболонки , крім grep є ще одне рішення :

Bash має власний движок регулярних виразів з версії 3.0, використовуючи =~оператор, як і 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, сподіваюсь, ти тут не розбираєш XML з регулярним виразом .. :)
joonas.fi

Це ще гірше. Я розбираю жахливий поєднання даних XML та FASTA (які обидва використовують >символ для зовсім інших цілей), як це було викладено програмним забезпеченням для відчуження великих розмірів SANSparallel. Звичайно, обидва формати розплетені без перешкод. Тому неможливо викинути якусь стандартну бібліотеку XML. І в цій точці коду я використовую Bash regex, тому що мені потрібно витягти лише пару даних, і 2 regex роблять роботу набагато краще, ніж писати виділений аналізатор для цього безладу. #LifeInBioinformatics
DrYak

Іншими словами: є момент, коли витягнути 1 єдине число простіше зробити з регекс-ротаном, ніж танцювати все танго 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


Для мене вирішальним було зрозуміти, що \ n не працює з sed. Існує причина, що ви використовуєте [0-9] + там. :)
користувач27432

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

0

Це здійснить те, що ви просите, але я не думаю, що це те, чого ви дійсно хочете. Я поставив .*передню частину вигулу, щоб з'їсти що-небудь перед матчем, але це жадібна операція, тому це відповідає лише передостанньому \wсимволу в рядку.

Зауважте, що вам потрібно уникнути паронів і +.

sed 's/.*\(\w\).\+/\1/' myfile.txt
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.