Grep: Звездочка (*) не всегда работает

Grep: Звездочка (*) не всегда работает

Если я выполню grep для документа, содержащего следующее:

ThisExampleString

...для выражения This*Stringили *Stringничего не возвращается. Однако This*возвращает строку выше, как и ожидалось.

Не имеет значения, заключено ли выражение в кавычки.

Я думал, что звездочка указывает на любое количество неизвестных символов? Почему она работает только если она находится в начале выражения? Если это задумано, что мне использовать вместо выражений This*Stringи *String?

решение1

Звездочка вобычные выраженияозначает «соответствие предыдущему элементу 0 или более раз».

В вашем конкретном случае с grep 'This*String' file.txt, вы пытаетесь сказать: «Эй, grep, сопоставь мне слово Thi, за которым следует строчная буква sноль или более раз, за ​​которой следует слово String«. Строчные буквы sнигде не встречаются в Example, поэтому grep игнорирует ThisExampleString.

В случае grep '*String' file.txt, вы говорите "grep, сопоставь мне пустую строку -- буквально ничего -- предшествующую слову String". Конечно, это не то, как ThisExampleStringпредполагается читать. (Естьдругие возможные значения(Вы можете попробовать это с -Eфлагом и без него, но ни одно из значений не соответствует тому, что вам действительно нужно.)

Зная, что это .означает «любой отдельный символ», мы могли бы сделать это: grep 'This.*String' file.txt. Теперь команда grep правильно прочтет это: Thisза которым следует любой символ (думайте об этом как о наборе символов ASCII), повторенный любое количество раз, за ​​которым следует String.

решение2

Метасимвол *в BRE 1 s, ERE 1 s и PCRE 1 s соответствует 0 или более вхождений ранее сгруппированного шаблона (если сгруппированный шаблон предшествует метасимволу *), 0 или более вхождений предыдущего класса символов (если класс символов предшествует метасимволу *) или 0 или более вхождений предыдущего символа (если ни сгруппированный шаблон, ни класс символов не предшествуют метасимволу *);

Это означает, что в This*Stringшаблоне, являющемся *метасимволом, которому не предшествует ни сгруппированный шаблон, ни класс символов, *метасимвол соответствует 0 или более вхождений предыдущего символа (в данном случае символа s):

% cat infile               
ThisExampleString
ThisString
ThissString
% grep 'This*String' infile
ThisString
ThissString

Чтобы сопоставить 0 или более вхождений любого символа, необходимо сопоставить 0 или более вхождений метасимвола ., который соответствует любому символу:

% cat infile               
ThisExampleString
% grep 'This.*String' infile
ThisExampleString

Метасимвол *в BRE и ERE всегда «жадный», т.е. он будет соответствовать самому длинному совпадению:

% cat infile
ThisExampleStringIsAString
% grep -o 'This.*String' infile
ThisExampleStringIsAString

Это может быть нежелательным поведением; в противном случае вы можете включить grepдвижок PCRE (используя опцию -P) и добавить ?метасимвол, который при размещении после *метасимволов +и изменяет их жадность:

% cat infile
ThisExampleStringIsAString
% grep -Po 'This.*?String' infile
ThisExampleString

1: Базовые регулярные выражения, расширенные регулярные выражения и регулярные выражения, совместимые с Perl

решение3

Одно из объяснений можно найти здесьсвязь:

Звездочка " *" в регулярных выражениях означает не то же самое, что и в подстановочных знаках; это модификатор, который применяется к предшествующему одиночному символу или выражению, например [0-9]. Звездочка соответствует нулю или более того, что ей предшествует. Таким образом, [A-Z]*соответствует любому количеству заглавных букв, включая ни одной, а [A-Z][A-Z]*соответствует одной или нескольким заглавным буквам.

решение4

*имеет особое значение как оболочкаподстановкасимвол («подстановочный знак») и как регулярное выражениеметасимвол. Вы должны принять во внимание оба варианта, хотя если выцитироватьваше регулярное выражение, то вы можете запретить оболочке обрабатывать его особым образом и гарантировать, что она передаст его без измененийgrep. Хотявродеконцептуально схоже, то, что *означает для оболочки, совершенно отличается от того, что это означает для grep.

Первыйоболочка рассматривает *как подстановочный знак.

Вы сказали:

Не имеет значения, заключено ли выражение в кавычки.

Это зависит от того, какие файлы существуют в каталоге, в котором вы находитесь, когда запускаете команду. Для шаблонов, содержащих разделитель каталогов /, это может зависеть от того, какие файлы существуют во всей вашей системе. Вы всегда должныцитироватьрегулярные выражения для grep--иодинарные кавычкиобычно лучше всего--пока неВы уверены, что вас все устраиваетдевять типов потенциально удивительных преобразованийоболочка в противном случае выполняетдовыполнение grepкоманды.

Когда оболочка встречает *символ, который не являетсяцитируется, он принимает это как «ноль или более любых символов» изаменяет слово, которое его содержитсо списком имен файлов, соответствующих шаблону. (Имена файлов, начинающиеся с , .исключаются, если только сам шаблон не начинается с. или(Вы в любом случае настроили свою оболочку так, чтобы она включала их.) Это известно какподстановка--а также по именамрасширение имени файлаирасширение имени пути.

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

Причина этого в том,иногдане проблема — и в вашем конкретном случае, по крайней мередо сих пор, это не было -- это *останется в покоеесли все нижеперечисленное верно:

  1. Былонетфайлы, имена которых совпали. ...Иливы отключили подстановку в вашей оболочке, обычно с помощью set -fили эквивалента set -o noglob. Но это необычно, и вы, вероятно, знаете, что сделали это.

  2. Вы используете оболочку, поведение которой по умолчанию заключается в том, чтобы оставить ее *в покое, если нет соответствующих имен файлов. Это случай Bash, который вывероятнос использованием, но не во всех оболочках в стиле Bourne. (Поведение по умолчанию в популярной оболочке Zsh, например, для globs либо(а)расширить или(б)выдает ошибку.)...Иливы изменили поведение своей оболочки — способ, которым это делается, различается в разных оболочках.

  3. Вы нев противном случаесказал вашей оболочке разрешить замену глобусов наничегокогда нет соответствующих файлов, или не выдать сообщение об ошибке в этой ситуации. В Bash это было бы сделано путем включения nullglobилиfailglob вариант оболочки, соответственно.

Иногда можно положиться на #2 и #3, но редко можно положиться на #1. grepКоманда с шаблоном без кавычек, которая работает сейчас, может перестать работать, если у вас есть другие файлы или если вы запускаете ее из другого места.Заключите в кавычки ваше регулярное выражение, и проблема исчезнет.

Затемкоманда grepтрактует *как квантификатор.

Другие ответы, такие как этиСергей Колодяжныйиот kos--также затрагивают этот аспект этого вопроса, несколько иными способами. Поэтому я призываю тех, кто еще не прочитал их, сделать это либо до, либо после прочтения остальной части этого ответа.

Если предположить, что *это происходит в grep (что должно быть обеспечено кавычками), grepто это означает, чтоэлемент, который ему предшествуетможет происходить любое количество раз, а не обязательно должно произойти ровно один раз. Это все еще может произойти один раз. Или это может не присутствовать вообще. Или это может повторяться. Текст, который соответствуетлюбойиз этих возможностей будут сопоставлены.

Что я подразумеваю под словом «предмет»?

  • Одинхарактер. Так как bсоответствует литералу b, b*соответствует нулю или более bs, таким образом ab*cсоответствует ac, abc, abbc, abbbc, и т. д.

    Аналогично, поскольку.соответствует любому символу, .*соответствует нулю или более символов1, таким образом, a.*cсоответствует ac, akc, ahjglhdfjkdlgjdfkshlgc, даже acccccchjckhccи т. д.Или

  • Акласс персонажа. Так как [xy]соответствует xили y, [xy]*соответствует нулю или более символов, где каждый из них является либо xили y, таким образом p[xy]*qсоответствует pq, pxq, pyq, pxxq, pxyq, pyxq, pyyq, pxxxq, pxxyq, и т. д.

    Это также относится ксокращенные формыклассов символов, таких как \w, \W, \s, и \S. Поскольку \wсоответствует любому символу слова, \w*соответствует нулю или более символам слова.Или

  • Агруппа. Так как \(bar\)соответствует bar, \(bar\)*соответствует нулю или более bars, таким образом foo\(bar\)*bazсоответствует foobaz, foobarbaz, foobarbarbaz, foobarbarbarbaz, и т. д.

    С опциями -Eor обрабатывает ваше регулярное выражение как-PgrepЭРЭилиПКРЕсоответственно, а не какБРЭ, а затем группы окружены ( )вместо \( \), поэтому тогда вы должны использовать (bar)вместо \(bar\)и foo(bar)bazвместо foo\(bar\)baz.

man grepдает разумно доступное объяснение синтаксиса BRE и ERE в конце, а также перечисляет все принимаемые параметры командной строки grepв начале. Я рекомендую эту страницу руководства как ресурс, а такжедокументация GNU Grepиэтот учебный/справочный сайт(ссылки на несколько страниц я привел выше).

Для тестирования и обучения grepя рекомендую вызывать его с шаблоном, но без имени файла. Затем он принимает входные данные с вашего терминала. Введите строки; строки, которые будут возвращены вам, содержат текст, соответствующий вашему шаблону. Чтобы выйти, нажмите Ctrl+ Dв начале строки, что означает конец ввода. (Или вы можете нажать Ctrl+ C, как в большинстве программ командной строки.) Например:

grep 'This.*String'

Если вы используете --colorфлаг, grepбудет выделен конкретныйчастиваших строк, которые соответствуют вашему регулярному выражению, что очень полезно как для выяснения того, что делает регулярное выражение, так и для поиска того, что вы ищете, когда вы это сделаете. По умолчанию у пользователей Ubuntu есть псевдоним Bash, который вызывает grep --color=autoзапуск — что достаточно для этой цели — при запуске grepиз командной строки, так что вам, вероятно, даже не нужно будет передавать --colorвручную.

1 Поэтому .*в регулярном выражении означает то, что *означает в оболочке glob. Однако разница в том, что grepавтоматически печатает строки, содержащие ваше совпадениев любом местев них, поэтому его обычно нет необходимости использовать .*в начале или конце регулярного выражения.

Связанный контент