Как мне сопоставить строку с регулярным выражением в Bash?


166

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

Я использую операторы if elif then, которые проверяют имя файла, чтобы увидеть, чем оно заканчивается, и я не могу заставить его соответствовать, используя метасимволы регулярных выражений.

Чтобы сохранить постоянное переписывание сценария, который я использую в командной строке '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поскольку это безопаснее и мощнее. См. В чем разница между test, [и [[? для деталей.


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

7
Я пытался использовать кавычки на регулярное выражение и не удалось; этот ответ помог сделать эту работу, check="^a.*c$";if [[ "abc" =~ $check ]];then echo match;fiнам нужно хранить регулярные выражения в var
Aquarius Power

Также отметим, что регулярное выражение (как в perl) НЕ должно быть в скобках: [[ sed-4.2.2.tar.bz2 == "*tar.bz2" ]]не будет работать.
Певик

18
FWIW, синтаксис для отрицания (то есть не совпадает ) [[ ! foo =~ bar ]].
Skippy le Grand Gourou

1
Тире не поддерживает -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в tar GNU или pв tar BSD явно указывать ему автоматически выводить тип сжатия из расширения. В противном случае 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
Это чрезвычайно опасно; он ведет себя только без неопределенного поведения, потому что в текущем каталоге нет файлов с буквенной подстрокой «pattern». Давайте создадим несколько файлов с такими именами, и расширение подстроки будет соответствовать файлам и ужасно сломать все разноцветными heisenbugs.
i336_

Но я провел эксперимент: с файлами `1pattern, pattern pattern2 и pattern в текущем каталоге. Этот скрипт работает как положено. Не могли бы вы предоставить мне ваш результат теста? @ i336_
Хуан Кортес,

2
@ i336: я так не думаю. Внутри [[ ... ]]шаблон rhs glob не расширяется в соответствии с текущим каталогом, как обычно.
user1934428

@ i336_ Нет. Внутри [[...]]Bash не выполняет расширение имени файла. В руководстве по bashWord 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.