Поиск и замена в bash с использованием регулярных выражений

Я видел этот пример:

hello=ho02123ware38384you443d34o3434ingtod38384day
echo ${hello//[0-9]/}

Который следует за этим синтаксисом: ${variable//pattern/replacement}

К сожалению, поле pattern похоже не поддерживает полный синтаксис regex (если я использую . или \s, например, он пытается сопоставить буквенные символы).

Как я могу искать/заменять строку, используя полный синтаксис regex?

Ответ 1

Используйте sed:

MYVAR=ho02123ware38384you443d34o3434ingtod38384day
echo "$MYVAR" | sed -e 's/[a-zA-Z]/X/g' -e 's/[0-9]/N/g'
# prints XXNNNNNXXXXNNNNNXXXNNNXNNXNNNNXXXXXXNNNNNXXX

Обратите внимание, что последующие -e обрабатываются по порядку. Кроме того, флаг g для выражения будет соответствовать всем вхождениям на входе.

Вы также можете выбрать свой любимый инструмент, используя этот метод, например, perl, awk, например:

echo "$MYVAR" | perl -pe 's/[a-zA-Z]/X/g and s/[0-9]/N/g'

Это может позволить вам делать больше творческих соответствий... Например, в приведенном выше фрагменте замена чисел не будет использоваться, если не будет совпадения в первом выражении (из-за ленивости and оценки). И, конечно, у вас есть полная языковая поддержка Perl для выполнения ваших ставок...

Ответ 2

Фактически может выполняться в чистом bash:

hello=ho02123ware38384you443d34o3434ingtod38384day
re='(.*)[0-9]+(.*)'
while [[ $hello =~ $re ]]; do
  hello=${BASH_REMATCH[1]}${BASH_REMATCH[2]}
done
echo "$hello"

... дает...

howareyoudoingtodday

Ответ 3

Эти примеры также работают в bash не нужно использовать sed:

#!/bin/bash
MYVAR=ho02123ware38384you443d34o3434ingtod38384day
MYVAR=${MYVAR//[a-zA-Z]/X} 
echo ${MYVAR//[0-9]/N}

вы также можете использовать выражения скобок символьного класса

#!/bin/bash
MYVAR=ho02123ware38384you443d34o3434ingtod38384day
MYVAR=${MYVAR//[[:alpha:]]/X} 
echo ${MYVAR//[[:digit:]]/N}

Выход

XXNNNNNXXXXNNNNNXXXNNNXNNXNNNNXXXXXXNNNNNXXX

То, что @Lanaru хотел узнать, если я правильно понял вопрос, почему "полные" или расширения PCRE \s\S\w\W\d\D и т.д. не работают как поддерживаемые в php ruby ​​python и т.д. Эти расширения от Perl-совместимых регулярные выражения (PCRE) и могут быть несовместимы с другими формами регулярных выражений на основе оболочки.

Они не работают:

#!/bin/bash
hello=ho02123ware38384you443d34o3434ingtod38384day
echo ${hello//\d/}


#!/bin/bash
hello=ho02123ware38384you443d34o3434ingtod38384day
echo $hello | sed 's/\d//g'

вывод со всеми буквальными "d" символами удален

ho02123ware38384you44334o3434ingto38384ay

но следующее работает как ожидалось

#!/bin/bash
hello=ho02123ware38384you443d34o3434ingtod38384day
echo $hello | perl -pe 's/\d//g'

Выход

howareyoudoingtodday

Надеюсь, что это немного разъяснит ситуацию, но если вы еще не запутались, почему бы вам не попробовать это в Mac OS X, где включен флаг REG_ENHANCED:

#!/bin/bash
MYVAR=ho02123ware38384you443d34o3434ingtod38384day;
echo $MYVAR | grep -o -E '\d'

В большинстве вариантов * nix вы увидите только следующий вывод:

d
d
d

NJoy!

Ответ 4

Если вы делаете повторные вызовы и занимаетесь производительностью, этот тест показывает, что метод BASH ~ 15 раз быстрее, чем forking для sed и, вероятно, любой другой внешний процесс.

hello=123456789X123456789X123456789X123456789X123456789X123456789X123456789X123456789X123456789X123456789X123456789X

P1=$(date +%s)

for i in {1..10000}
do
   echo $hello | sed s/X//g > /dev/null
done

P2=$(date +%s)
echo $[$P2-$P1]

for i in {1..10000}
do
   echo ${hello//X/} > /dev/null
done

P3=$(date +%s)
echo $[$P3-$P2]

Ответ 5

Используйте [[:digit:]] (обратите внимание на двойные скобки) в качестве шаблона:

$ hello=ho02123ware38384you443d34o3434ingtod38384day
$ echo ${hello//[[:digit:]]/}
howareyoudoingtodday

Просто хотел обобщить ответы (особенно @nickl- fooobar.com/info/66978/...).