101 votes

Substitution dans le fichier texte **sans** expressions régulières

Je dois substituer un peu de texte à l'intérieur d'un fichier texte par un remplacement. Habituellement, je ferais quelque chose comme

sed -i 's/text/replacement/g' chemin/vers/le/fichier

Le problème est que à la fois texte et remplacement sont des chaînes complexes contenant des tirets, des barres obliques, des barres obliques inversées, des guillemets, etc. Si j'échappe à tous les caractères nécessaires à l'intérieur de texte, la chose devient rapidement illisible. D'un autre côté, je n'ai pas besoin de la puissance des expressions régulières : je dois juste substituer le texte littéralement.

Y a-t-il un moyen de faire une substitution de texte sans utiliser d'expressions régulières avec une commande bash ?

Il serait plutôt trivial d'écrire un script qui fait cela, mais je me dis qu'il devrait déjà exister quelque chose.

16voto

eavsteen Points 21
export FIND='rechercher ceci'
export REPLACE='remplacer par ceci'
ruby -p -i -e "gsub(ENV['FIND'], ENV['REPLACE'])" chemin/vers/fichier

C'est la seule solution totalement sûre ici, car :

  • C'est une substitution statique, pas une expression régulière, donc pas besoin d'échapper quoi que ce soit (donc, supérieur à l'utilisation de sed)
  • Cela ne cassera pas si votre chaîne contient le caractère } (donc, supérieur à une solution Perl soumise)
  • Cela ne cassera pas avec n'importe quel caractère, car ENV['FIND'] est utilisé, pas $FIND. Avec $FIND ou votre texte en ligne dans le code Ruby, vous pourriez rencontrer une erreur de syntaxe si votre chaîne contenait un ' non échappé.

12voto

Mark Points 251

Quand vous n'avez pas besoin de la puissance des expressions régulières, ne l'utilisez pas. C'est bien.
Mais, ce n'est pas vraiment une expression régulière.

sed 's|motif_littéral|chaine_de_remplacement|g'

Donc, si / pose problème, utilisez | et vous n'avez pas besoin d'échapper l'ancien.

PS : En ce qui concerne les commentaires, consultez également cette réponse Stackoverflow sur Échapper une chaîne pour un motif de recherche sed.


Mise à jour : Si cela ne vous dérange pas d'utiliser Perl, essayez-le avec \Q et \E comme ceci,

 perl -pe 's|\Qmotif_littéral\E|chaine_de_remplacement|g'

@RedGrittyBrick a également suggéré une astuce similaire avec une syntaxe Perl plus forte dans un commentaire <a href="https://superuser.com/questions/422459/substitution-in-text-file-without-regular-expressions#comment484003_422478">ici</a> ou <a href="https://stackoverflow.com/a/1048144/70482">ici</a>

11voto

La commande replace fera cela.

https://linux.die.net/man/1/replace

Changer sur place:

replace text remplacement -- chemin/vers/le/fichier

Vers stdout:

replace text remplacement < chemin/vers/le/fichier

Exemple:

$ replace '.*' '[^a-z ]{1,3}' < r1: /.*/g
> r2: /.*/gi
> EOF
r1: /[^a-z ]{1,3}/g
r2: /[^a-z ]{1,3}/gi

La commande replace est fournie avec MySQL ou MariaDB.

5voto

VasyaNovikov Points 3169

Vous pouvez le faire en convertissant automatiquement les modèles dans leur forme échappée. Comme ceci :

keyword_raw=$'1\n2\n3'
keyword_regexp="$(printf '%s' "$keyword_raw" | sed -e 's/[]\/$*.^|[]/\\&/g' | sed ':a;N;$!ba;s,\n,\\n,g')"
# keyword_regexp est maintenant '1\/2\/3'

replacement_raw=$'2\n3\n4'
replacement_regexp="$(printf '%s' "$replacement_raw" | sed -e 's/[\/&]/\\&/g' | sed ':a;N;$!ba;s,\n,\\n,g')"
# replacement_regexp est maintenant '2\/3\/4'

echo $'a/b/c/1\n2\n3/d/e/f' | sed -e "s/$keyword_regexp/$replacement_regexp/"
# la dernière commande affichera 'a/b/c/2\n3\n4/d/e/f'

Crédits pour cette solution vont ici : https://stackoverflow.com/questions/407523/escape-a-string-for-a-sed-replace-pattern

Note1: ceci fonctionne uniquement pour les mots-clés non vides. Les mots-clés vides ne sont pas acceptés par sed (sed -e 's//replacement/').

Note2: malheureusement, je ne connais pas un outil populaire qui n'utiliserait PAS les regexp pour résoudre le problème. Vous pouvez écrire un tel outil en Rust ou en C, mais ce n'est pas inclus par défaut.

3voto

Abbas Points 3737

Vous pourriez également utiliser le mécanisme \Q de Perl pour "citer (désactiver) les métacaractères du motif"

perl -pe 'BEGIN {$text = q{votre */texte/?va"ici"}} s/\Q$text\E/remplacement/g'

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X