Як я можу зіставити рядок з регулярним виразом у Bash?


166

Я намагаюся написати Баш скрипт , який містить функцію так , коли дан .tar, .tar.bz2, і .tar.gzт.д. файл , він використовує дьоготь з відповідними перемикачами , щоб розпакувати файл.

Я використовую, якщо elif, то висловлювання, які перевіряють ім'я файлу, щоб побачити, чим він закінчується, і я не можу змусити його збігатися з використанням метахарактерів regex.

Щоб зберегти постійно переписувати скрипт, який я використовую 'test' у командному рядку, я вважав, що твердження нижче повинно працювати, я перепробував усі можливі комбінації дужок, лапок і метахараторів, і все одно він не вдається.

test sed-4.2.2.tar.bz2 = tar\.bz2$; echo $?
(this returns 1, false)

Я впевнений, що проблема проста, і я шукав всюди, але не можу зрозуміти, як це зробити. Хтось знає, як я можу це зробити?

Відповіді:


268

Для відповідності регулярним виразам вам потрібно скористатися =~оператором.

Спробуйте це:

[[ sed-4.2.2.tar.bz2 =~ tar.bz2$ ]] && echo matched

Крім того, ви можете використовувати підстановку (замість регулярних виразів) з ==оператором:

[[ sed-4.2.2.tar.bz2 == *tar.bz2 ]] && echo matched

Якщо портативність не викликає занепокоєння, я рекомендую використовувати [[замість цього, [або testвін безпечніший і потужніший. Див. Яка різниця між тестом, [та [[? для деталей.


7
Будьте уважні до відповідності загальних підстановок у другому прикладі. Всередині [[]] * не розширюється, як це зазвичай, щоб відповідати назви файлів у поточному каталозі, що відповідає шаблону. Ваш приклад працює, але насправді легко переосмислити і помилково вважати, що * означає зіставити що-небудь у будь-який контекст. Це працює лише так всередині [[]]. В іншому випадку він розширюється до існуючих імен файлів.
Алан Портер

7
Я намагався використовувати лапки на регулярному виразі і не вдався; ця відповідь допомогла зробити цю роботу check="^a.*c$";if [[ "abc" =~ $check ]];then echo match;fiнам потрібно зберігати регулярний вираз у варі
Водолій Сила

Також зауважте, що regexp (як в perl) НЕ повинен бути в дужках: [[ sed-4.2.2.tar.bz2 == "*tar.bz2" ]]не працював.
півік

18
FWIW, синтаксис заперечення (тобто не відповідає ) є [[ ! foo =~ bar ]].
Skippy le Grand Gourou

1
dash не підтримує -n 1параметр, а також не вводить його автоматично в $REPLYзмінну. Стережись!

54

Функція для цього

extract () {
  if [ -f $1 ] ; then
      case $1 in
          *.tar.bz2)   tar xvjf $1    ;;
          *.tar.gz)    tar xvzf $1    ;;
          *.bz2)       bunzip2 $1     ;;
          *.rar)       rar x $1       ;;
          *.gz)        gunzip $1      ;;
          *.tar)       tar xvf $1     ;;
          *.tbz2)      tar xvjf $1    ;;
          *.tgz)       tar xvzf $1    ;;
          *.zip)       unzip $1       ;;
          *.Z)         uncompress $1  ;;
          *.7z)        7z x $1        ;;
          *)           echo "don't know '$1'..." ;;
      esac
  else
      echo "'$1' is not a valid file!"
  fi
}

Інша примітка

У відповідь на Водолій Силу у коментарі вище, We need to store the regex on a var

Змінна BASH_REMATCH встановлюється після того, як ви співставите вираз, і $ {BASH_REMATCH [n]} буде відповідати n-й групі, загорненій в дужки, тобто в наступних ${BASH_REMATCH[1]} = "compressed"та${BASH_REMATCH[2]} = ".gz"

if [[ "compressed.gz" =~ ^(.*)(\.[a-z]{1,5})$ ]]; 
then 
  echo ${BASH_REMATCH[2]} ; 
else 
  echo "Not proper format"; 
fi

(Регекс, наведений вище, не повинен бути дійсним для іменування та розширення файлів, але він працює для прикладу)


також зауважте, що за допомогою BSD tar ви можете використовувати "tar xf" для всіх форматів і не потребувати окремих команд або цієї функції взагалі.
Гарна людина

aна тарілці GNU або pна BSD tar, щоб чітко сказати, щоб він автоматично виводив тип стиснення з розширення. GNU tar не зробить це автоматично інакше, і я здогадуюсь з коментаря @GoodPerson, що BSD tar робить це за замовчуванням.
Марк К Коуан

7z можна розпакувати .. AR, ARJ, CAB, CHM, CPIO, CramFS, DMG, EXT, FAT, GPT, HFS, IHEX, ISO, LZH, LZMA, MBR, MSI, NSIS, NTFS, QCOW2, RAR, RPM, SquashFS , UDF, UEFI, VDI, VHD, VMDK, WIM, XAR і Z. см 7-zip.org
мош

14

У мене тут недостатньо респондентів для коментарів, тому я надсилаю нову відповідь, щоб покращити відповідь догбена. Крапка. в регексп

[[ sed-4.2.2.tar.bz2 =~ tar.bz2$ ]] && echo matched

насправді буде відповідати будь-якому символу, не тільки буквальній крапці, наприклад, 'tar.bz2'

[[ sed-4.2.2.tar4bz2 =~ tar.bz2$ ]] && echo matched
[[ sed-4.2.2.tar§bz2 =~ tar.bz2$ ]] && echo matched

або що-небудь, що не потребує втечі з "\". Тож повинен бути суворий синтаксис

[[ sed-4.2.2.tar.bz2 =~ tar\.bz2$ ]] && echo matched

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

[[ sed-4.2.2.tar.bz2 =~ \.tar\.bz2$ ]] && echo matched

9

Оскільки ви використовуєте bash, вам не потрібно створювати дочірній процес для цього. Ось одне рішення, яке виконує його повністю в межах bash:

[[ $TEST =~ ^(.*):\ +(.*)$ ]] && TEST=${BASH_REMATCH[1]}:${BASH_REMATCH[2]}

Пояснення: Групи до і після послідовності "двокрапка і один або більше пробілів" зберігаються оператором відповідності шаблонів у масиві BASH_REMATCH.


1
Зауважте, що індекс 0 містить повну відповідність, а індекс 1 і 2 містять групові збіги.
Райнер Шварце

3
if [[ $STR == *pattern* ]]
then
    echo "It is the string!"
else
    echo "It's not him!"
fi

Для мене працює! GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu)


1
Це надзвичайно небезпечно; він веде себе лише без невизначеної поведінки для вас, оскільки у вас немає файлів у поточному каталозі з назвою буквального підрядка "шаблоном". Далі створіть такі файли з таким іменем, і розширення підрядків відповідатиме файлам і все страшенно розбиватиме різнокольоровими heisenbugs.
i336_

Але я провів експеримент: з файлами `1pattern, pattern pattern2 та pattern у поточному каталозі. Цей сценарій працює як очікувалося. Чи можете ви надати мені свій тестовий результат? @ i336_
juan cortez

2
@ i336: Я не думаю, що так. У межах [[ ... ]]глобальної моделі резус не розширюється відповідно до поточного каталогу, як це зазвичай робиться.
користувач1934428

@ i336_ Ні. [[...]]Bash не виконує розширення імені файлу. У посібнику з bash,Word splitting and filename expansion are not performed on the words between the [[ and ]];
jinbeom hong

@jinbeomhong: TIL. Це добре знати, дякую!
i336_

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.