Регулярное выражение 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, это не работает - что я делаю не так?
Ответы
Ответ 1
Возможно,\S и \s не поддерживаются или вы не можете разместить их вокруг [ ]
. Попробуйте использовать этот формат:
^Disk[[:space:]]+/dev[^[:space:]]+:[[:space:]]+[^[:space:]]+
EDIT
Кажется, вы действительно хотите получить соответствующие поля. Я сделал этот сценарий проще, но я не уверен, что это именно то, что вам нужно:
#!/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.
Ответ 2
из man bash
Доступен дополнительный бинарный оператор = ~, с тем же приоритет как == и! =. Когда это используется, строка справа от оператор выделил расширенное регулярное выражение extended regular expression и сопоставил его (как в регулярном выражении (3)).
ERE не поддерживает прогнозирование/отставание. Однако они есть в вашем коде ((?!Disk)
).
Вот почему ваше регулярное выражение не будет соответствовать, как вы ожидали.
Ответ 3
Поскольку это часто задаваемые вопросы, позвольте мне перечислить несколько конструкций, которые не поддерживаются в Bash, и как обойти их, где есть простой обходной путь.
Есть несколько диалектов регулярных выражений в общем использовании. Тот, который поддерживается Bash, является вариантом расширенных регулярных выражений. Это отличается от, например, что поддерживают многие онлайн-тестеры регулярных выражений, что часто является более современным вариантом Perl 5/PCRE.
- Bash не поддерживает
\d
\D
\s
\S
\w
\W
- их можно заменить эквивалентами классов символов POSIX [[:digit:]]
, [^[:digit:]]
, [[:space:]]
, [^[:space:]]
, [_[:alnum:]]
и [^_[:alnum:]]
соответственно. (Обратите внимание на последний случай, когда класс символов POSIX [:alnum:]
дополнен подчеркиванием, чтобы в точности соответствовать сокращению Perl \w
.)
- Bash не поддерживает не жадное сопоставление. Иногда вы можете заменить
a.*?b
чем-то вроде a[^ab]*b
, чтобы получить похожий эффект на практике, хотя они не совсем эквивалентны.
- Bash не поддерживает скобки без захвата
(?:...)
. В тривиальном случае просто используйте взятие скобок вместо (...)
; хотя, конечно, если вы используете группы захвата и/или обратные ссылки, это перенумерует ваши группы захвата.
- Bash не поддерживает обходные пути, такие как
(?<=before)
или (?!after)
, и фактически что-либо с (?
является расширением Perl. Простого общего обходного пути для них не существует, хотя вы часто можете перефразировать свою проблему в ту, в которой можно избежать обходных путей.
Ответ 4
Я знаю, что вы уже "решили" это, но ваша первоначальная проблема была, вероятно, такой же простой, как и отсутствие цитирования $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 не передает переменные, а расширяет их.
Ответ 5
Кроме того, [\s\S]
эквивалентен .
, то есть любому символу. На моей оболочке [^\s]
работает, но не [\S]
.