Зіставлення регулярних виразів у операторі Bash if


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
Натомість поставте одинарні лапки навколо завдання. Подвійні лапки не захистять спеціальні символи належним чином.
триплі

Багато, Чарльз! Це все ще добре, якщо не поміщати його в змінну, але це НЕ повинно бути в лапках взагалі! Наприклад: [[ $var =~ .* ]]для регулярного виразу матчу .*(що завгодно). Я думаю, що якщо ви використовуєте лапки, самі цитати вважаються частиною регулярного виразу ...
Стефан

4
gotcha короткий зміст Я знайшов: (1.) зберегти шаблон у змінній, використовуючи одинарні лапки pattern='^hello[0-9]*$'(2.) у виразі подвійного квадрата, якщо вам потрібне відповідність регулярних виразів, НЕ цитувати шаблон, оскільки цитування ВИМКНУЄ відповідність шаблону регулярних виразів. (Тобто вираз [[ "$x" =~ $pattern ]]буде відповідати , використовуючи регулярний вираз і вираз [[ "$x" =~ "$pattern" ]]відключає регулярні вирази і еквівалентно[[ "$x" == "$pattern" ]] ).
Тревор Бойд Сміт,

Відповіді:


177

Є кілька важливих речей, які слід знати про [[ ]]будівництво Баша . Перший:

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

Друге:

Доступний додатковий двійковий оператор, '= ~', ... рядок праворуч від оператора вважається розширеним регулярним виразом і відповідно відповідає йому ... Будь-яка частина шаблону може бути вказана в лапках, щоб змусити його відповідати як рядок .

Отже, $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
Просто хочу зазначити, що "! ~" Оператор "не відповідає" не відповідає дійсності. Або вживайте, if ! [[ $x =~ $y ]]абоif [[ ! $x =~ $y ]]
алкоголь

shellchecker не погоджується ...SC2076: Don't quote rhs of =~, it'll match literally rather than as a regex.
Леонардо

4
@leonard: чим це відрізняється від мого твердження "ви не можете цитувати змінну розширення" та коментаря "Це не працює"? Що в цьому незрозуміло?
Річі

1
@jinbeomhong: сам вираз поділяється на слова, як зазвичай, за допомогою пробілів. Але розширення параметрів і команд не є розділеними словами.
Річі

1
@jinbeomhong: Я не кажу нічого, що відрізняється від посібника з Bash. " слова між [[і ]]" аналізуються з тексту програми, так само командні рядки аналізуються на слова. Однак, на відміну від командних рядків, слова не розбиваються після розширення.
Річі

26

Якщо хтось хотів приклад із використанням змінних ...

#!/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:]]+$ ]]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.