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


68

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 les deux textet replacementsont des chaînes complexes contenant des tirets, des barres obliques, blackslashes, citations et ainsi de suite. Si j'échappe à tous les caractères nécessaires, textla chose 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.

Existe-t-il un moyen de substituer du texte sans utiliser d'expressions régulières avec une commande bash?

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


Nécessaire de le faire par bash? Une solution simpliste serait d'ouvrir dans Word et de faire unfind and replace all
Akash

17
@akash Parce que les systèmes qui ont bashtoujours été livrés avec Microsoft Word? ;) Non je rigole. Cependant, l’opérateur peut souhaiter effectuer cette opération sur une machine distante ou pour un lot de fichiers.
Slhck

@slhck :) Eh bien, je suppose que gedit devrait avoir une option similaire
Akash

Une option serait d' une certaine façon échapper à tout correctement avant de passer à sed, ce qui est probablement un effort futile compte tenu de tous les commutateurs et les différences de plate - forme.
l0b0

Réponses:


6

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 Stackoverflow sur Échap une chaîne pour un modèle de recherche sed .


Mise à jour: si vous utilisez Perl, essayez avec \Qet \Ecomme ceci,
perl -pe 's|\Qliteral_pattern\E|replacement_string|g'
RedGrittyBricka également suggéré une astuce similaire avec une syntaxe Perl plus forte dans un commentaire ici


Merci, je ne connaissais pas la différence entre / et |
Andrea

64
Je ne suis pas sûr que cette réponse soit utile ... La seule différence entre s|||et s///est que le caractère de séparation est différent et qu'un personnage n'a pas besoin de s'échapper. Vous pourriez également faire s###. Le vrai problème ici est que l'OP ne veut pas avoir à s'inquiéter d'échapper au contenu de literal_pattern(ce qui n'est pas du tout littéral et sera interprété comme une regex).
Benj

15
Cela n'évitera pas l'interprétation d'autres caractères spéciaux. Et si chercher 1234.*aaaavec votre solution il correspond beaucoup plus que prévu 1234\.\*aaa.
Matteo

20
Cette réponse ne doit pas être acceptée
Steven Lu

2
Cela manque tout à fait. Le texte à faire correspondre peut contenir n'importe quelle étrangeté. Dans mon cas, c'est un mot de passe aléatoire. Vous savez comment ça se passe
Christian Bongiorno

13
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 regexp, pas besoin d'échapper à quoi que ce soit (donc supérieur à utiliser sed)
  • Cela ne casse pas si votre chaîne contient un caractère }(donc supérieur à une solution Perl soumise)
  • Cela ne rompt avec aucun personnage, car il ENV['FIND']est utilisé, non $FIND. Avec $FINDou votre texte inséré dans le code Ruby, vous risquez de rencontrer une erreur de syntaxe si votre chaîne contient un caractère non échappé '.

Je devais utiliser export FIND='find this; export REPLACE='replace with this';dans mon script bash pour que ENV['FIND']et ENV['replace']avoir les valeurs attendues. Je remplaçais de très longues chaînes cryptées dans un fichier. C'était juste le billet.
DMfll

C'est une bonne réponse parce que c'est fiable et que le rubis est omniprésent. Sur la base de cette réponse, j'utilise maintenant ce script shell .
loevborg

Malheureusement, cela ne fonctionne pas lorsque FIND contient plusieurs lignes.
Adrelanos

Rien ne l'empêche de travailler avec plusieurs lignes dans FIND. Utilisez les guillemets doubles \ n.
Nowaker le

7

La replacecommande 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 replacecommande est fournie avec MySQL ou MariaDB.


3
tenir compte du fait que le remplacement est obsolète et ne sera peut-être plus disponible à l'avenir
Rogelio

1
Pourquoi diable une telle commande de base vient-elle avec une base de données?
masterxilo

3
@masterxilo Une meilleure question pourrait être: pourquoi une telle commande de base ne vient-elle pas avec les systèmes d'exploitation modernes? ;-)
Mark Thomson


3

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.


2

Vous pouvez le faire en échappant à vos habitudes. 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. Les mots clés vides ne sont pas acceptés par sed ( sed -e 's//replacement/').

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.


Cela manque complètement le point de l'OP. Évidemment, vous pouvez échapper au modèle, mais pour certains modèles, cela est fastidieux.
icecreamsword

@icecreamsword Avez-vous lu ma réponse au-dessous de la première ligne? Le script s'échappe automatiquement .
VasyaNovikov

1

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" "$@"
}

Ne fonctionne pas avec les nouvelles lignes. Aussi n'aide pas à échapper à newlines avec \n. Toute solution?
Adrelanos

1

Vous pouvez utiliser str_replace de php :

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

Remarque: vous devrez néanmoins échapper aux guillemets simples 'et doubles ".


0

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

Heres un plus "presque" manière de travail.

Utilisez vi ou vim.

Créez un fichier texte avec votre substitution dedans:

:% sno / ma chaîne de recherche \\ "-: # 2; g ('. j'); \\"> / my replacestring = \\ "bac) (o: # 46; \\"> /
:X

puis exécutez vi ou vim depuis la ligne de commande:

vi -S commandfile.txt path/to/the/file

:% sno est la commande vi pour rechercher et remplacer sans magie.

/ est mon séparateur choisi.

: x enregistre et quitte vi.

Vous devez éviter les barres obliques inverses '\' La barre oblique vers l'avant '/' peut être remplacée par exemple par un point d'interrogation '?' ou quelque chose d'autre qui ne fait pas partie de votre recherche ou remplace-string, pipe '|' n'a pas fonctionné pour moi tho.

ref: https://stackoverflow.com/questions/6254820/perform-a-non-regex-search-replace-in-vim https://vim.fandom.com/wiki/Search_without_need_to_escape_slash http://linuxcommand.org/ lc3_man_pages / vim1.html

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.