Пошук тексту між двома певними символами чи рядками


17

Скажіть, у мене є такі рядки:

*[234]*
*[23]*
*[1453]*

де *позначає будь-яку рядок (крім рядка форми [number]). Як я можу розібрати ці рядки за допомогою утиліти командного рядка та витягнути число між дужками?

У більш загальному сенсі , який з цих інструментів cut, sed, grepабо було awkб доцільно для такого завдання?

Відповіді:


16

Якщо у вас є GNU grep, ви можете скористатися його -oопцією для пошуку регулярного вираження та виведення лише відповідної частини. (Інші реалізації grep можуть показувати лише весь рядок.) Якщо в одному рядку є кілька збігів, вони друкуються окремими рядками.

grep -o '\[[0-9]*\]'

Якщо ви хочете лише цифри, а не дужки, це трохи складніше; вам потрібно використовувати твердження нульової ширини: регулярний вираз, який відповідає порожній рядку, але лише в тому випадку, якщо йому передують або дотримуються, якщо це залежно від випадку, дужки. Затвердження нульової ширини доступні лише в синтаксисі Perl.

grep -P -o '(?<=\[)[0-9]*(?=\])'

За допомогою sed вам потрібно вимкнути друк -nі зіставити всю лінійку та зберегти лише відповідну частину. Якщо в одному рядку є кілька можливих збігів, друкується лише останній збіг. Докладніші відомості про використання тут див. У розділі Витягнення регулярного вираження, збіганого із «sed», без друку навколишніх символів .

sed -n 's/^.*\(\[[0-9]*\]\).*/\1/p'

або якщо ви хочете лише цифри, а не дужки:

sed -n 's/^.*\[\([0-9]*\)\].*/\1/p'

Без цього grep -oPerl є інструментом вибору тут, якщо ви хочете чогось простого і зрозумілого. У кожному рядку ( -n), якщо рядок містить відповідність \[[0-9]*\], друкуйте його ( $&) та новий рядок ( -l).

perl -l -ne '/\[[0-9]*\]/ and print $&'

Якщо ви хочете лише цифри, покладіть дужки в регулярні вирази для розмежування групи та друкуйте лише цю групу.

perl -l -ne '/\[([0-9]*)\]/ and print $1'

PS Якщо ви хочете вимагати лише однієї або декількох цифр між дужками, перейдіть [0-9]*на Perl [0-9][0-9]*або на нього [0-9]+.


Все добре, крім того, що він хоче "витягнути число між дужками". Я думаю, що "крім [number]" означає, за винятком[0-9]
Пітер.О

1
@ Peter.OI розумів "крім [число]" означає, що немає інших частин рядка такої форми. Але я відредагував свою відповідь, щоб показати, як друкувати лише цифри, про всяк випадок.
Жил "ТАК - перестань бути злим"

1
Ці perlтвердження регексу виглядають дуже корисно! Я читав про них, побачивши, як ви використовуєте як зворотні, так і прямі твердження, навіть із грейпом (я відключився від того, що ви можете вибрати двигун регулярного вибору). Я з цього моменту приділяю трохи більше часу регексу Perl. Дякую ... PS .. Я щойно прочитав у man grep... "Це надзвичайно експериментально та привабливо -P може попередити про безпроблемні функції". ... Я сподіваюся, що це не означає нестабільний (?) ...
Пітер.О

5

Ви не можете це зробити cut.

  1. tr -c -d '0123456789\012'
  2. sed 's/[^0-9]*//g'
  3. awk -F'[^0-9]+' '{ print $1$2$3 }'
  4. grep -o -E '[0-9]+'

tr є найбільш природним пристосуванням для проблеми і, ймовірно, працює найшвидше, але я думаю, вам знадобляться гігантські введення, щоб розділити будь-який із цих варіантів з точки зору швидкості.


Для СЕД, ^.*жадібний і споживає все , крім останньої цифри, і +має бути , \+або ж використовувати POSIX \([0-9][0-9]*\).... і в будь-якому випадку 's/[^0-9]*//g'працює так само добре, ... Thanks for the тр -c` приклад, але не те, що кінцеві \012surperfluous?
Пітер.О

@Peter Дякую, що це зробив. Я б поклявся, що я перевірив приклад седу. :( Я змінив його на вашу версію. Щодо \012: це потрібно інакше trбуде їсти нові рядки.
Кайл Джонс

Ага ... Я бачив це як \0, 1, 2(або навіть \, 0, 1, 2). Я недостатньо добре налаштований на восьмикутник, здається .. Дякую.
Пітер.О

4

Якщо ви маєте в виду витягти набір послідовних цифр між НЕ-цифровими символами, я думаю , sedі awkкращі (хоча grepце також може дати вам збіглися символи):

sed: ви, звичайно, можете порівнювати цифри, але, можливо, цікаво зробити навпаки, видалити нецифрові цифри (працює, якщо є лише одне число на рядок):

$ echo nn3334nn | sed -e 's/[^[[:digit:]]]*//g'
3344

grep: ви можете відповідати послідовним цифрам

$ echo nn3334nn | grep -o '[[:digit:]]*'
3344

Я не навожу приклад awkтому, що я маю нульовий досвід роботи з ним; цікаво зауважити, що, хоч sedшвейцарський ніж, grepдає вам простіший, більш зрозумілий спосіб зробити це, який також працює для більше ніж одне число у кожному рядку введення ( -oєдиний друкує відповідні частини вводу, кожен з них у власному рядку):

$ echo dna42dna54dna | grep -o '[[:digit:]]*'
42
54

Так само , як порівняння, ось sedeqivalent з «більш ніж один номер в рядку» наприклад grep -o '[[:digit:]]*'. . . sed -nr '/[0-9]/{ s/^[^[0-9]*|[^0-9]*$//g; s/[^0-9]+/\n/g; p}'... (+1)
Пітер.О

2

Оскільки було сказано, що цього неможливо зробити cut, я покажу, що легко можна виробити рішення, яке, принаймні, не гірше, ніж деякі інші, хоча я не схвалюю використання cutяк "найкращого" (або навіть особливо хороше) рішення. Слід сказати, що будь-яке рішення, яке не шукає конкретно цифр *[і ]*навколо, робить спрощення припущень і тому схильне до відмови на прикладах, більш складних, ніж у тих, що подаються запитувачем (наприклад, цифри зовні *[і ]*які не повинні бути показані). Це рішення перевіряє принаймні дужки, і його можна розширити, щоб перевірити також зірочки (залишено читачем як вправу):

cut -f 2 -d '[' myfile.txt | cut -f 1 -d ']'

При цьому використовується -dопція, яка вказує роздільник. Очевидно, ви також можете вставити у cutвираз замість читання з файлу. Хоча cutце, мабуть, досить швидко, оскільки він простий (без двигуна регулярного випромінювання), вам доведеться викликати його принаймні двічі (або ще кілька разів для перевірки *), що створює деякий накладний процес. Єдиною реальною перевагою цього рішення є те, що воно досить читабельне, особливо для випадкових користувачів, які недостатньо добре розбираються у конструктах регулярних виразів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.