використовувати регулярний вираз в if-умові в bash


88

Цікаво загальне правило використовувати регулярний вираз у реченні if у bash?

Ось приклад

$ gg=svm-grid-ch  
$ if [[ $gg == *grid* ]] ; then echo $gg; fi  
svm-grid-ch  
$ if [[ $gg == ^....grid* ]] ; then echo $gg; fi  
$ if [[ $gg == ....grid* ]] ; then echo $gg; fi  
$ if [[ $gg == s...grid* ]] ; then echo $gg; fi  
$   

Чому останні три не відповідають?

Сподіваюся, ви могли б дати якомога більше загальних правил, не лише для цього прикладу.

Відповіді:


128

При використанні шаблону глобуса знак питання представляє один символ, а зірочка - послідовність з нуля або більше символів:

if [[ $gg == ????grid* ]] ; then echo $gg; fi

При використанні регулярного виразу крапка представляє один символ, а зірочка - нуль або більше попереднього символу. Отже, " .*" представляє нуль або більше будь-якого символу, " a*" являє собою нуль або більше "a", " [0-9]*" представляє нуль або більше цифр. Іншим корисним (серед багатьох) є знак плюс, який представляє один або кілька попередніх символів. Отже, " [a-z]+" представляє один або кілька малих літерних символів (у мові C - та деяких інших).

if [[ $gg =~ ^....grid.*$ ]] ; then echo $gg; fi

Отже, існує два способи узгодження рядків: шаблон глобуса та регулярний вираз? Чи використовується glob pettern не лише для імен файлів? У bash, коли використовувати шаблон glob, а коли регулярний вираз? Дякую!
Тім,

1
@Tim: Глобізм доступний у більшості або всіх версіях Bash. Зіставлення регулярних виразів доступне лише у версії 3 і вище, але я б рекомендував використовувати його лише у версії 3.2 та новіших. Регекси набагато універсальніші, ніж глобінг.
Призупинено до подальшого повідомлення.



7

Додавання цього рішення grepта базових shвбудованих програм для тих, хто цікавиться більш портативним рішенням (незалежно від bashверсії; також працює зі звичайними старими sh, на платформах, що не належать до Linux, тощо)

# GLOB matching
gg=svm-grid-ch    
case "$gg" in
   *grid*) echo $gg ;;
esac

# REGEXP    
if echo "$gg" | grep '^....grid*' >/dev/null ; then echo $gg ; fi    
if echo "$gg" | grep '....grid*' >/dev/null ; then echo $gg ; fi    
if echo "$gg" | grep 's...grid*' >/dev/null ; then echo $gg ; fi    

# Extended REGEXP
if echo "$gg" | egrep '(^....grid*|....grid*|s...grid*)' >/dev/null ; then
  echo $gg
fi    

Деякі grepвтілення також підтримують -qопцію (тихий) як альтернативу переспрямуванню на /dev/null, але перенаправлення знову є найбільш портативним.


забув закриття ")" для egrep
ghostdog74

5
Використовуйте grep -qзамість grep >/dev/null.
bfontaine

3

@OP,

Чи використовується glob pettern не лише для імен файлів?

Ні, шаблон "glob" використовується не лише для імен файлів. Ви також використовуєте його для порівняння рядків. У своїх прикладах ви можете використовувати case / esac для пошуку шаблонів рядків.

 gg=svm-grid-ch 
 # looking for the word "grid" in the string $gg
 case "$gg" in
    *grid* ) echo "found";;
 esac

 # [[ $gg =~ ^....grid* ]]
 case "$gg" in ????grid*) echo "found";; esac 

 # [[ $gg =~ s...grid* ]]
 case "$gg" in s???grid*) echo "found";; esac

У bash, коли використовувати шаблон glob, а коли регулярний вираз? Дякую!

Regex є більш універсальним і "зручним", ніж "шаблони глобусів", однак якщо ви не виконуєте складних завдань, які "глобінг / розширений глобінг" не може забезпечити легко, тоді немає необхідності використовувати регулярний вираз. Regex не підтримуються для версії bash <3.2 (як згадував Деніс), але ви все одно можете використовувати розширений глобінг (за допомогою налаштування extglob). для розширеного глобінгу дивіться тут і кілька простих прикладів тут .

Оновлення для OP: Приклад пошуку файлів, які починаються з 2 символів (крапки "." Означає 1 символ), а потім "g" за допомогою регулярного виразу

наприклад вихід

$ shopt -s dotglob
$ ls -1 *
abg
degree
..g

$ for file in *; do [[ $file =~ "..g" ]] && echo $file ; done
abg
degree
..g

У наведеному вище файли узгоджуються, оскільки їхні імена містять 2 символи, за якими йде "g". (тобто..g ).

Еквівалент глобінгу буде приблизно таким: (дивіться посилання на значення ?і *)

$ for file in ??g*; do echo $file; done
abg
degree
..g

Дякую ghostdog74. У Bash з версією вищою ніж 3.2, чи можна використовувати регулярний вираз для заміни шаблону глобуса скрізь, де він з’являється? Або регулярний вираз можна вживати лише за якихось особливих обставин? Наприклад, я виявив, що "ls ?? g" працює, а "ls ..g" - ні.
Тім

Вам не буде заважати використовувати регулярний вираз, якщо в цьому є необхідність. Тобі вирішувати. Зверніть увагу, синтаксис регулярних виразів відрізняється від синтаксису глобальної оболонки. так ls ..gне працює. Ви говорите оболонці шукати файл із іменем ..g. Що ж стосується вивчення синтаксису регулярних виразів, ви можете спробувати perldoc perlretut, perldoc perlrequickчи зробити info sedв командному рядку.
ghostdog74
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.