242 votes

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

J'ai déjà vu cet exemple :

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

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

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

Comment rechercher/remplacer une chaîne de caractères en utilisant la syntaxe regex complète ?

0 votes

J'ai trouvé une question connexe ici : stackoverflow.com/questions/5658085/

3 votes

POUR INFORMATION, \s ne fait pas partie de la syntaxe standard des expressions régulières définies par POSIX (ni BRE ni ERE) ; il s'agit d'une extension de PCRE, qui n'est généralement pas disponible dans l'interpréteur de commandes. [[:space:]] est l'équivalent le plus universel.

2 votes

\s peut être remplacé par [[:space:]] En passant, je vous signale qu'il n'y a pas d'autre solution que de faire appel à l'aide, . por ? et extglob, des extensions au langage de base du shell peuvent être utilisées pour des choses telles que des sous-groupes optionnels, des groupes répétés, etc.

223voto

jheddings Points 10510

Utilice sed :

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

Il convient de noter que les -e sont traitées dans l'ordre. De même, les g pour l'expression correspondra à toutes les occurrences dans l'entrée.

Vous pouvez également choisir votre outil préféré en utilisant cette méthode, c'est-à-dire perl, awk, etc :

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

Cela peut vous permettre de faire des rapprochements plus créatifs... Par exemple, dans l'extrait ci-dessus, le remplacement numérique ne serait pas utilisé s'il n'y avait pas de correspondance sur la première expression (en raison de la paresse de l'utilisateur). and évaluation). Et bien sûr, vous disposez du support linguistique complet de Perl pour vous aider...

0 votes

Pour autant que je sache, il n'effectue qu'un seul remplacement. Existe-t-il un moyen de remplacer toutes les occurrences du motif comme le fait le code que j'ai posté ?

0 votes

J'ai mis à jour ma réponse pour démontrer les remplacements multiples ainsi que la correspondance globale des motifs. Faites-moi savoir si cela vous aide.

0 votes

Merci beaucoup ! Par curiosité, pourquoi êtes-vous passé d'une version d'une ligne (dans votre réponse initiale) à une version de deux lignes ?

162voto

Charles Duffy Points 34134

En fait, ceci 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"

...rendements...

howareyoudoingtodday

4 votes

Quelque chose me dit que vous allez les adorer : stackoverflow.com/questions/5624969/ \=)

0 votes

=~ est la clé. Mais un peu maladroit, étant donné la réaffectation dans la boucle. La solution de @jheddings 2 ans plus tôt est une autre bonne option - appeler sed ou perl).

4 votes

Appel sed o perl est judicieux si l'on utilise chaque invocation pour traiter plus d'une ligne d'entrée. L'invocation d'un tel outil à l'intérieur d'une boucle, par opposition à l'utilisation d'une boucle pour traiter son flux de sortie, est imprudente.

134voto

nickl- Points 1772

Ces exemples fonctionnent également dans 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 entre crochets de la classe de caractères

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

sortie

XXNNNNNXXXXNNNNNXXXNNNXNNXNNNNXXXXXXNNNNNXXX

Ce que @Lanaru voulait savoir cependant, si je comprends bien la question, c'est pourquoi les extensions "full" ou PCRE \s\S\w\W\d\D etc. ne fonctionnent pas comme prévu dans php ruby python etc. Ces extensions sont issues des expressions régulières compatibles avec Perl (PCRE) et peuvent ne pas être compatibles avec d'autres formes d'expressions régulières basées sur l'interpréteur de commandes.

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 littéraux "d" 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 sur Mac OS X qui a l'indicateur REG_ENHANCED 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

nJoy !

6 votes

Pardon ? ${foo//$bar/$baz} est no POSIX.2 BRE ou syntaxe ERE -- il s'agit d'un filtrage de type fnmatch()-style.

8 votes

... donc, considérant que ${hello//[[:digit:]]/} fonctionne, si nous voulions filtrer uniquement les chiffres précédés de la lettre o , ${hello//o[[:digit:]]*} aurait un comportement totalement différent de celui attendu (puisque dans les modèles fnmatch, * correspond à tous les caractères, plutôt que de modifier l'élément immédiatement précédent pour qu'il soit 0 ou plus).

2 votes

Véase pubs.opengroup.org/onlinepubs/9699919799/utilities/ (et tout ce qu'il incorpore par référence) pour les spécifications complètes de fnmatch.

14voto

Josiah DeWitt Points 703

Si vous effectuez des appels répétés et que les performances vous préoccupent, ce test révèle que la méthode BASH est ~15x plus rapide que la bifurcation vers 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]

1 votes

Si vous êtes intéressé par les méthodes de réduction des fourches, recherchez le mot nouveauConnecteur en cette réponse à Comment définir une variable à la sortie d'une commande en Bash ?

11voto

yegeniy Points 836

Utilice [[:digit:]] (notez les doubles parenthèses) comme modèle :

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

Je voulais juste résumer les réponses (surtout celles de @nickl-). https://stackoverflow.com/a/22261334/2916086 ).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X