Регулярное выражение Bash - похоже, не совпадает с \s, \S и т. Д.

У меня есть скрипт, который пытается получить блоки информации от gparted.

Мои данные выглядят так:

Disk /dev/sda: 42.9GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number  Start   End     Size    Type     File system     Flags
 1      1049kB  316MB   315MB   primary  ext4            boot
 2      316MB   38.7GB  38.4GB  primary  ext4
 3      38.7GB  42.9GB  4228MB  primary  linux-swap(v1)

log4net.xml
Model: VMware Virtual disk (scsi)
Disk /dev/sdb: 42.9GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number  Start   End     Size    Type     File system     Flags
 1      1049kB  316MB   315MB   primary  ext4            boot
 5      316MB   38.7GB  38.4GB  primary  ext4
 6      38.7GB  42.9GB  4228MB  primary  linux-swap(v1)

Я использую регулярное выражение, чтобы разбить это на два дисковых блока

^ Диск (/dev[\S]+):((?! Диск)[\s\S])*

Это работает с многострочным.

Когда я проверяю это в скрипте bash, я не могу найти совпадения с \ s или \ S. Что я делаю не так?

Я проверяю это с помощью сценария, как:

data=`cat disks.txt`
morematches=1
x=0
regex="^Disk (/dev[\S]+):((?!Disk)[\s\S])*"

if [[ $data =~ $regex ]]; then
echo "Matched"
while [ $morematches == 1 ]
do
        x=$[x+1]
        if [[ ${BASH_REMATCH[x]} != "" ]]; then
                echo $x "matched" ${BASH_REMATCH[x]}
        else
                echo $x "Did not match"
                morematches=0;
        fi

done

fi

Однако, когда я прохожу тестирование частей регулярного выражения, всякий раз, когда я сопоставляю \ s или \S, это не работает - что я делаю неправильно?

6 ответов

Решение

Возможно, \S и \s не поддерживаются или вы не можете разместить их [ ], Попробуйте использовать этот формат вместо:

^Disk[[:space:]]+/dev[^[:space:]]+:[[:space:]]+[^[:space:]]+

РЕДАКТИРОВАТЬ

Кажется, вы действительно хотите получить соответствующие поля. Я сделал сценарий более простым, но я не уверен, что это ваш намеченный результат:

#!/bin/bash 

regex='^Disk[[:space:]]+(/dev[^[:space:]]+):[[:space:]]+(.*)'

while read line; do
    [[ $line =~ $regex ]] && echo "${BASH_REMATCH[1]} matches ${BASH_REMATCH[2]}."
done < disks.txt

Который производит

/dev/sda matches 42.9GB.
/dev/sdb matches 42.9GB.

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

Есть несколько диалектов регулярных выражений в общем использовании. Тот, который поддерживается Bash, является вариантом расширенных регулярных выражений. Это отличается от того, что поддерживают многие онлайн-тестеры регулярных выражений, что часто является более современным вариантом Perl 5 / PCRE.

  • Баш не поддерживает \d\D\s\S\w\W - они могут быть заменены эквивалентами класса символов POSIX [[:digit:]], [^[:digit:]], [[:space:]], [^[:space:]], [_[:alnum:]], а также [^_[:alnum:]]соответственно. (Обратите внимание на последний случай, когда [:alnum:] Класс символов POSIX дополнен подчеркиванием, чтобы быть точно эквивалентным Perl \w стенографии.)
  • Bash не поддерживает не жадное сопоставление. Вы можете иногда заменить a.*?b с чем-то вроде a[^ab]*b чтобы получить аналогичный эффект на практике, хотя эти два не совсем эквивалентны.
  • Bash не поддерживает такие взгляды, как (?<=before) или же (?!after) и на самом деле что-нибудь с (? является расширением Perl. Простого общего обходного пути для них не существует, хотя вы часто можете перефразировать свою проблему в ту, в которой можно избежать обходных путей.

От man bash

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

ERE не поддерживает просмотр вперед / назад. Однако они есть в вашем коде ((?!Disk)).

Вот почему ваше регулярное выражение не будет соответствовать, как вы ожидали.

Bash поддерживает то, что regcomp(3)поддерживает в вашей системе. Реализация Glibc поддерживает и другие, но из-за того, что Bash использует кавычки для бинарных операторов, вы не можете закодировать правильный код. \sнапрямую, независимо от того, что вы делаете:

      [[ 'a   b' =~ a[[:space:]]+b ]] && echo ok # OK
[[ 'a   b' =~ a\s+b ]] || echo fail        # Fail
[[ 'a   b' =~ a\\s+b ]] || echo fail       # Fail
[[ 'a   b' =~ a\\\s+b ]] || echo fail      # Fail

Для этого гораздо проще работать с переменной шаблона:

      pattern='a\s+b'
[[ 'a   b' =~ $pattern ]] && echo ok # OK

Я знаю, что вы уже "решили" это, но ваша первоначальная проблема была, вероятно, так же проста, как не цитирование $regex в вашем тесте. то есть:

if [[ $data =~ "$regex" ]]; then

Расширение переменной Bash просто добавит в строку, а пробел в вашем исходном регулярном выражении прервет тест, потому что:

regex="^Disk (/dev[\S]+):((?!Disk)[\s\S])*"
if [[ $data =~ $regex ]]; then

является эквивалентом:

if [[ $data =~ ^Disk (/dev[\S]+):((?!Disk)[\s\S])* ]]; then

и bash / test будет весело проводить время, интерпретируя бонусный аргумент и все эти мета-символы без кавычек.

Помните, что bash не передает переменные, а расширяет их.

Также, [\s\S] эквивалентно .любой символ. На моей раковине [^\s] работает но не [\S],

Другие вопросы по тегам