web-dev-qa-db-fra.com

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

Je dois remplacer un texte dans un fichier texte par un autre. D'habitude je ferais quelque chose comme

sed -i 's/text/replacement/g' path/to/the/file

Le problème est que text et replacement sont des chaînes complexes contenant des tirets, des barres obliques, des barres obliques, des guillemets, etc. Si j'échappe à tous les caractères nécessaires dans text, cela devient rapidement illisible. D'autre part, je n'ai pas besoin de la puissance des expressions régulières: je dois juste remplacer le texte littéralement.

Y a-t-il un moyen de faire la substitution de texte sans en utilisant des expressions régulières avec une commande bash?

Il serait assez trivial d’écrire un script qui le fasse, mais j’imagine qu’il devrait déjà exister quelque chose.

64
Andrea

Lorsque vous n'avez pas besoin du pouvoir des expressions régulières, ne l'utilisez pas. C'est bon.
Mais ce n’est pas vraiment une expression régulière .

sed 's|literal_pattern|replacement_string|g'

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

ps: à propos des commentaires, voir aussi cette réponse de Stackoverflow sur Échappe une chaîne pour le modèle de recherche sed .


Mise à jour: Si vous utilisez Perl , essayez-le avec \Q et \E comme ceci,
Perl -pe 's|\Qliteral_pattern\E|replacement_string|g'
RedGrittyBrick a également suggéré une astuce similaire avec la syntaxe Perl plus forte dans un commentaire ici

5
nik
export FIND='find this'
export REPLACE='replace with this'
Ruby -p -i -e "gsub(ENV['FIND'], ENV['REPLACE'])" path/to/file

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

  • C'est une sous-station statique, pas une expression rationnelle, pas besoin d'échapper à quoi que ce soit (donc supérieure à l'utilisation de sed)
  • Cela ne casse pas si votre chaîne contient } char (donc supérieure à une solution Perl soumise)
  • Cela ne rompt avec aucun caractère, car ENV['FIND'] est utilisé, pas $FIND. Avec $FIND ou votre texte inséré dans le code Ruby, vous risquez de rencontrer une erreur de syntaxe si votre chaîne contient un ' non échappé.
13
Nowaker

La commande replace le fera.

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

Changement de lieu:

replace text replacement -- path/to/the/file

Pour stdout:

replace text replacement < path/to/the/file

Exemple:

$ replace '.*' '[^a-z ]{1,3}' <<EOF
> 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.

7
Derek Veit

Vous pouvez également utiliser le mécanisme \Q de Perl pour " citer des métacaractères de modèle "

Perl -pe 'BEGIN {$text = q{your */text/?goes"here"}} s/\Q$text\E/replacement/g'
3
glenn jackman

découvrez mon script Perl. il fait exactement ce dont vous avez besoin sans utilisation implicite ou explicite d’expression régulière:

https://github.com/Samer-Al-iraqi/Linux-str_replace

str_replace Search Replace File # replace in File in place

STDIN | str_replace Search Replace # to STDOUT

très pratique non? J'ai dû apprendre à Perl pour le faire. parce que j'en ai vraiment besoin.

3
Samer Ata

Vous pouvez le faire en en échappant vos patterns. Comme ça:

keyword_raw='1/2/3'
keyword_regexp="$(printf '%s' "$keyword_raw" | sed -e 's/[]\/$*.^|[]/\\&/g')"
# keyword_regexp is now '1\/2\/3'

replacement_raw='2/3/4'
replacement_regexp="$(printf '%s' "$replacement_raw" | sed -e 's/[\/&]/\\&/g')"
# replacement_regexp is now '2\/3\/4'

echo 'a/b/c/1/2/3/d/e/f' | sed -e "s/$keyword_regexp/$replacement_regexp/"
# the last command will print 'a/b/c/2/3/4/d/e/f'

Les crédits pour ces solutions vont ici: https://stackoverflow.com/questions/407523/escape-a-string-for-a-sed-replace-pattern

Note1: cela ne fonctionne que pour les mots-clés non vides. Sed (sed -e 's//replacement/') n'accepte pas les mots clés vides.

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

2
VasyaNovikov

Vous pouvez utiliser php's str_replace :

php -R 'echo str_replace("\|!£$%&/()=?^\"'\''","replace",$argn),PHP_EOL;'<input.txt >output.txt

Remarque: Toutefois, vous devrez toujours échapper les guillemets simples ' et les guillemets doubles ".

1
simlev

J'ai rassemblé quelques autres réponses et proposé ceci:

function unregex {
   # This is a function because dealing with quotes is a pain.
   # http://stackoverflow.com/a/2705678/120999
   sed -e 's/[]\/()$*.^|[]/\\&/g' <<< "$1"
}
function fsed {
   local find=$(unregex "$1")
   local replace=$(unregex "$2")
   shift 2
   # sed -i is only supported in GNU sed.
   #sed -i "s/$find/$replace/g" "$@"
   Perl -p -i -e "s/$find/$replace/g" "$@"
}
1
Xiong Chiamiov

Node.JS équivalent de @Nowaker:

export FNAME='moo.txt'
export FIND='search'
export REPLACE='rpl'
node -e 'fs=require("fs");fs.readFile(process.env.FNAME,"utf8",(err,data)=>{if(err!=null)throw err;fs.writeFile(process.env.FNAME,data.replace(process.env.FIND,process.env.REPLACE),"utf8",e=>{if(e!=null)throw e;});});'
0
A T