web-dev-qa-db-fra.com

Rechercher et remplacer en bash en utilisant des expressions régulières

J'ai vu cet exemple:

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

Ce qui suit cette syntaxe: ${variable//pattern/replacement}

Malheureusement, le champ pattern ne semble pas prendre en charge la syntaxe regex complète (si j'utilise . ou \s, par exemple, il essaie de faire correspondre les caractères littéraux).

Comment puis-je rechercher/remplacer une chaîne en utilisant la syntaxe regex complète?

133
Lanaru

Utilisez sed :

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

Notez que les -e _ suivants sont traités dans l'ordre. De plus, l'indicateur g de l'expression correspondra à toutes les occurrences de l'entrée.

Vous pouvez également choisir votre outil préféré en utilisant cette méthode, à savoir Perl, awk, par exemple:

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

Cela vous permettra peut-être de faire plus de correspondances créatives ... Par exemple, dans la capture ci-dessus, le remplacement numérique ne sera pas utilisé à moins d'une correspondance avec la première expression (en raison de lazy and evaluation). Et bien sûr, vous disposez du support linguistique complet de Perl pour répondre à vos attentes ...

141
jheddings

Ceci en fait peut être fait en pur bash:

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

... donne ...

howareyoudoingtodday
117
Charles Duffy

Ces exemples fonctionnent aussi en bash sans avoir besoin d'utiliser sed:

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

vous pouvez également utiliser les expressions de crochet de classe de caractères

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

sortie

XXNNNNNXXXXNNNNNXXXNNNXNNXNNNNXXXXXXNNNNNXXX

Ce que @Lanaru a voulu savoir, cependant, si je comprends bien la question, c’est pourquoi les extensions "complètes" ou PCRE \s\S\w\W\d\D etc ne fonctionnent pas comme pris en charge dans php Ruby python etc. Ces extensions proviennent d'expressions régulières (PCRE) compatibles Perl et peuvent ne pas être compatibles avec d'autres formes d'expressions régulières basées sur Shell.

Ceux-ci ne fonctionnent pas:

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


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

sortie avec tous les caractères "d" littéraux supprimés

ho02123ware38384you44334o3434ingto38384ay

mais ce qui suit fonctionne comme prévu

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

sortie

howareyoudoingtodday

J'espère que cela clarifie un peu plus les choses, mais si vous n'êtes pas encore confus, pourquoi ne pas essayer ceci sur Mac OS X où l'indicateur REG_ENHANCED est activé:

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

Sur la plupart des versions de * nix, vous ne verrez que la sortie suivante:

d
d
d

nonJoy!

80
nickl-

Si vous effectuez des appels répétés et que vous êtes préoccupé par les performances, ce test révèle que la méthode BASH est environ 15 fois plus rapide que la transmission à sed et probablement à tout autre processus externe.

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]
12
Josiah DeWitt

Utilisez [[:digit:]] (notez les doubles crochets) comme motif:

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

Je voulais juste résumer les réponses (en particulier les @ nickl https://stackoverflow.com/a/22261334/2916086 ).

4
yegeniy