Сопоставление регулярных выражений в операторе if Bash

86

Что я здесь сделал не так?

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

TEST="THIS is a TEST title with some numbers 12345 and special char *&^%$#"

if [[ "$TEST" =~ [^a-zA-Z0-9\ ] ]]; then BLAH; fi

Очевидно, это проверяет только верхние, нижние, числа и пробелы. Но не работает.

* ОБНОВИТЬ *

Думаю, мне следовало быть более конкретным. Вот настоящая строчка кода.

if [[ "$TITLE" =~ [^a-zA-Z0-9\ ] ]]; then RETURN="FAIL" && ERROR="ERROR: Title can only contain upper and lowercase letters, numbers, and spaces!"; fi

* ОБНОВИТЬ *

./anm.sh: line 265: syntax error in conditional expression
./anm.sh: line 265: syntax error near `&*#]'
./anm.sh: line 265: `  if [[ ! "$TITLE" =~ [a-zA-Z0-9 $%^\&*#] ]]; then RETURN="FAIL" && ERROR="ERROR: Title can only contain upper and lowercase letters, numbers, and spaces!"; return; fi'
Атомиклан
источник
Какую оболочку вы на самом деле используете? / bin / sh? / bin / bash? / bin / csh?
Виллем Ван Онсем
8
Безопаснее поместить регулярное выражение в переменную. re='...whatever...'; [[ $string =~ $re ]](без кавычек - это один из редких случаев, когда сломают то, что без них работало бы).
Чарльз Даффи
3
Вместо этого заключите задание в одинарные кавычки. Двойные кавычки не защитят специальные символы должным образом.
tripleee
Большое спасибо, Чарльз! Это все еще нормально, не помещать его в переменную, но это НЕ должно вообще цитироваться! Например: [[ $var =~ .* ]]для регулярного выражения соответствия .*(что угодно). Я предполагаю, что если вы используете кавычки, сами кавычки считаются частью регулярного выражения…
Стефан
4
Резюме gotcha Я нашел: (1.) сохраните шаблон в переменной, используя одинарные кавычки pattern='^hello[0-9]*$'(2.) в двойном квадрате выражения, если вам нужно сопоставление регулярных выражений, НЕ цитируйте шаблон, потому что цитирование ОТКЛЮЧАЕТ сопоставление шаблонов регулярных выражений. (т.е. выражение [[ "$x" =~ $pattern ]]будет соответствовать с использованием регулярного выражения, а выражение [[ "$x" =~ "$pattern" ]]отключает сопоставление регулярных выражений и эквивалентно[[ "$x" == "$pattern" ]] ).
Trevor Boyd Smith

Ответы:

177

Есть несколько важных вещей, которые нужно знать о [[ ]]конструкции bash . Первое:

Разделение слов и расширение имени пути не выполняется для слов между символами [[и ]]; Выполняются раскрытие тильды, раскрытие параметров и переменных, арифметическое раскрытие, подстановка команд, подстановка процессов и удаление кавычек.

Второе:

Доступен дополнительный бинарный оператор '= ~', ... строка справа от оператора считается расширенным регулярным выражением и соответствующим образом сопоставляется ... Любая часть шаблона может быть заключена в кавычки, чтобы заставить ее сопоставить в виде строки .

Следовательно, $vс обеих сторон =~будет расширено до значения этой переменной, но результат не будет разбит по словам или расширен по имени пути. Другими словами, совершенно безопасно оставлять расширения переменных без кавычек с левой стороны, но вы должны знать, что расширения переменных будут происходить с правой стороны.

Так что если вы пишете: [[ $x =~ [$0-9a-zA-Z] ]], то $0в регулярном выражении по праву будет расширены до регулярного выражения интерпретируется, который, вероятно , вызовет регулярное выражение , чтобы не компилировать (если не указано расширение $0целей с символом цифр или знаков препинания , чье ASCII значения меньше цифра). Если вы процитируете правую часть таким образом [[ $x =~ "[$0-9a-zA-Z]" ]], то правая часть будет рассматриваться как обычная строка, а не как регулярное выражение$0все равно будет расширена). Что вам действительно нужно в этом случае, так это[[ $x =~ [\$0-9a-zA-Z] ]]

Точно так же выражение между [[и ]]разбивается на слова перед интерпретацией регулярного выражения. Поэтому пробелы в регулярном выражении необходимо экранировать или заключать в кавычки. Если вы хотите , чтобы соответствовать буквы, цифры и пробелы вы можете использовать: [[ $x =~ [0-9a-zA-Z\ ] ]]. Другие символы также должны быть экранированы, например #, если они не указаны в кавычках, начнется комментарий. Конечно, вы можете поместить шаблон в переменную:

pat="[0-9a-zA-Z ]"
if [[ $x =~ $pat ]]; then ...

Многие люди предпочитают этот стиль для регулярных выражений, которые содержат множество символов, которые необходимо экранировать или заключать в кавычки для прохождения через лексер bash. Но будьте осторожны: в этом случае вы не можете указывать расширение переменной:

# This doesn't work:
if [[ $x =~ "$pat" ]]; then ...

Наконец, я думаю, что вы пытаетесь проверить, что переменная содержит только допустимые символы. Самый простой способ выполнить эту проверку - убедиться, что он не содержит недопустимого символа. Другими словами, такое выражение:

valid='0-9a-zA-Z $%&#' # add almost whatever else you want to allow to the list
if [[ ! $x =~ [^$valid] ]]; then ...

!отменяет тест, превращая его в оператор «не соответствует», а [^...]класс символов регулярного выражения означает «любой символ кроме ...».

Комбинация операторов расширения параметров и регулярных выражений может сделать синтаксис регулярных выражений bash «почти читаемым», но все же есть некоторые подводные камни. (Не существует всегда?) Во- первых, вы не могли бы поставить ]в $valid, даже если $validбыли указаны, за исключением самого начала. (Это правило регулярного выражения Posix: если вы хотите включить ]в класс символов, он должен идти в начале. -Может идти в начале или в конце, поэтому, если вам нужны оба ]и -, вам нужно начинать ]и заканчивать с -, что приводит к регулярному выражению «Я знаю , что я делаю» смайлик: [][-])

ричи
источник
6
Просто хочу отметить, что "! ~ Is the" не соответствует "оператор" не соответствует действительности. Либо употребляю, if ! [[ $x =~ $y ]]либоif [[ ! $x =~ $y ]]
алкоголь
shellchecker не согласен с этим ...SC2076: Don't quote rhs of =~, it'll match literally rather than as a regex.
Leonardo
4
@leonard: чем это отличается от моего утверждения «вы не можете цитировать расширение переменной» и комментария «Это не работает»? Что в этом непонятного?
rici
1
@jinbeomhong: само выражение, как обычно, разделяется на слова с использованием пробелов. Но расширения параметров и команд не разделяются по словам.
rici,
1
@jinbeomhong: Я не говорю ничего отличного от руководства по bash. « слова между [[и ]]» анализируются из текста программы точно так же, как командные строки разбираются на слова. Однако, в отличие от командных строк, слова не разделяются после раскрытия.
rici,
27

Если кому-то нужен пример с использованием переменных ...

#!/bin/bash

# Only continue for 'develop' or 'release/*' branches
BRANCH_REGEX="^(develop$|release//*)"

if [[ $BRANCH =~ $BRANCH_REGEX ]];
then
    echo "BRANCH '$BRANCH' matches BRANCH_REGEX '$BRANCH_REGEX'"
else
    echo "BRANCH '$BRANCH' DOES NOT MATCH BRANCH_REGEX '$BRANCH_REGEX'"
fi
Оливер Пирмэйн
источник
13

Я бы предпочел использовать [:punct:]для этого. Также a-zA-Z09-9может быть просто [:alnum:]:

[[ $TEST =~ ^[[:alnum:][:blank:][:punct:]]+$ ]]
консольбокс
источник