Если я выполню 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
.) Вы практически никогда не хотите, чтобы это произошло.
Причина этого в том,иногдане проблема — и в вашем конкретном случае, по крайней мередо сих пор, это не было -- это *
останется в покоеесли все нижеперечисленное верно:
Былонетфайлы, имена которых совпали. ...Иливы отключили подстановку в вашей оболочке, обычно с помощью
set -f
или эквивалентаset -o noglob
. Но это необычно, и вы, вероятно, знаете, что сделали это.Вы используете оболочку, поведение которой по умолчанию заключается в том, чтобы оставить ее
*
в покое, если нет соответствующих имен файлов. Это случай Bash, который вывероятнос использованием, но не во всех оболочках в стиле Bourne. (Поведение по умолчанию в популярной оболочке Zsh, например, для globs либо(а)расширить или(б)выдает ошибку.)...Иливы изменили поведение своей оболочки — способ, которым это делается, различается в разных оболочках.Вы нев противном случаесказал вашей оболочке разрешить замену глобусов наничегокогда нет соответствующих файлов, или не выдать сообщение об ошибке в этой ситуации. В Bash это было бы сделано путем включения
nullglob
илиfailglob
вариант оболочки, соответственно.
Иногда можно положиться на #2 и #3, но редко можно положиться на #1. grep
Команда с шаблоном без кавычек, которая работает сейчас, может перестать работать, если у вас есть другие файлы или если вы запускаете ее из другого места.Заключите в кавычки ваше регулярное выражение, и проблема исчезнет.
Затемкоманда grep
трактует *
как квантификатор.
Другие ответы, такие как этиСергей Колодяжныйиот kos--также затрагивают этот аспект этого вопроса, несколько иными способами. Поэтому я призываю тех, кто еще не прочитал их, сделать это либо до, либо после прочтения остальной части этого ответа.
Если предположить, что *
это происходит в grep (что должно быть обеспечено кавычками), grep
то это означает, чтоэлемент, который ему предшествуетможет происходить любое количество раз, а не обязательно должно произойти ровно один раз. Это все еще может произойти один раз. Или это может не присутствовать вообще. Или это может повторяться. Текст, который соответствуетлюбойиз этих возможностей будут сопоставлены.
Что я подразумеваю под словом «предмет»?
Одинхарактер. Так как
b
соответствует литералуb
,b*
соответствует нулю или болееb
s, таким образом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\)*
соответствует нулю или болееbar
s, таким образомfoo\(bar\)*baz
соответствуетfoobaz
,foobarbaz
,foobarbarbaz
,foobarbarbarbaz
, и т. д.С опциями
-E
or обрабатывает ваше регулярное выражение как-P
grep
ЭРЭилиПКРЕсоответственно, а не какБРЭ, а затем группы окружены(
)
вместо\(
\)
, поэтому тогда вы должны использовать(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
автоматически печатает строки, содержащие ваше совпадениев любом местев них, поэтому его обычно нет необходимости использовать .*
в начале или конце регулярного выражения.