Поиск текста между двумя конкретными символами или строками

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 здесь.

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

или если вам нужны только цифры, а не скобки:

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

Без grep -oэтого Perl - это инструмент выбора, если вы хотите что-то простое и понятное. В каждой строке ( -n), если строка содержит совпадение для \[[0-9]*\], выведите соответствие ( $&) и символ новой строки ( -l).

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

Если вам нужны только цифры, поместите скобки в регулярное выражение, чтобы разделить группу, и напечатайте только эту группу.

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

PS Если вы хотите использовать только одну или несколько цифр в скобках, измените [0-9]*на [0-9][0-9]*или на [0-9]+Perl.

Жиль "ТАК - прекрати быть злым"
источник
Все хорошо, кроме того , что он хочет , чтобы «извлечь число между скобками». Я думаю, что «кроме [number]» означает, кроме[0-9]
Peter.O
1
@ Peter.OI я понял, что «кроме [число]» означает, что нет других частей линии этой формы. Но я отредактировал свой ответ, чтобы показать, как печатать только цифры, на всякий случай.
Жиль "ТАК - перестань быть злым"
1
Те perlутверждения регулярного выражения выглядят действительно полезными! Я читал о них, увидев, что вы используете как обратные, так и прямые утверждения, даже в grep (я отключился от того факта, что вы можете выбрать движок регулярных выражений). С этого момента я буду уделять немного больше времени регулярному выражению Perl. Спасибо ... PS .. Я только что прочитал man grep... "Это очень экспериментально, и grep -P может предупредить о невыполненных функциях". ... Надеюсь, это не означает нестабильность (?) ...
Peter.O
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 является наиболее естественным решением проблемы и, вероятно, будет работать быстрее всего, но я думаю, что вам потребуются гигантские входные данные, чтобы разделить любой из этих вариантов с точки зрения скорости.

Кайл Джонс
источник
Что касается sed, ^.*он жадный и потребляет все, кроме последней цифры, и +должен использовать \+или же использовать posix \([0-9][0-9]*\).... и в любом случае 's/[^0-9]*//g'работает так же хорошо, ... Thanks for the пример tr -c`, но разве это не \012лишний трейлинг ?
Peter.O
@Peter Спасибо, что поймали это. Я бы поклялся, что проверил пример sed. :( Я изменил его на вашу версию. Относительно \012: это необходимо, иначе trбудет есть новые строки.
Кайл Джонс
Ага ... Я видел это как \0, 1, 2(или даже \, 0, 1, 2). Кажется, я недостаточно хорошо настроен на восьмеричное. Спасибо.
Peter.O
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
njsg
источник
Так же , как сравнение, вот sedeqivalent из «более чем один номер в строке» например grep -o '[[:digit:]]*'. , , sed -nr '/[0-9]/{ s/^[^[0-9]*|[^0-9]*$//g; s/[^0-9]+/\n/g; p}'... (+1)
Peter.O
2

Поскольку было сказано, что это невозможно сделать cut, я покажу, что легко можно найти решение, которое, по крайней мере, не хуже, чем некоторые другие, даже если я не одобряю использование в cutкачестве «лучшего» (или даже особенно хорошее) решение. Следует сказать, что любое решение, которое не ищет конкретно *[и ]*вокруг цифр, делает упрощающие допущения и, следовательно, склонно к отказу в примерах, более сложных, чем тот, который задан запрашивающим (например, цифры снаружи *[и ]*, которые не должны быть показаны). Это решение проверяет, по крайней мере, скобки, и оно может быть расширено, чтобы также проверить звездочки (оставленные в качестве упражнения для читателя):

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

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

Томас
источник