Ersetzung in Textdatei ** ohne ** reguläre Ausdrücke

68

Ich muss Text in einer Textdatei durch einen Ersatz ersetzen. Normalerweise würde ich sowas machen

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

Das Problem ist, dass beide textund replacementkomplexe Zeichenfolgen Bindestriche, Schrägstriche, schwarze Schrägstriche, Anführungszeichen usw. enthalten. Wenn ich alle notwendigen Zeichen in textder Sache entkomme , wird schnell unlesbar. Andererseits brauche ich keine regulären Ausdrücke: Ich muss den Text nur wörtlich ersetzen.

Gibt es eine Möglichkeit, Text ohne reguläre Ausdrücke mit einem bash-Befehl zu ersetzen ?

Es wäre ziemlich trivial, ein Skript zu schreiben, das dies tut, aber ich denke, es sollte schon etwas geben.

Andrea
quelle
Notwendig, um es durch Bash zu tun? Eine vereinfachende Lösung wäre, in Word zu öffnen und einefind and replace all
Akash
17
@akash Weil Systeme, die bashimmer mit Microsoft Word ausgeliefert wurden? ;) Nein, ich mache nur Scherze. Möglicherweise möchte das OP dies auf einem Remote-Computer oder für einen Stapel von Dateien tun.
Slhck
@slhck :) Nun, ich denke, gedit sollte eine ähnliche Option haben
Akash
Eine Option wäre, sich vor der Weitergabe irgendwie korrekt von allem zu lösen sed, was angesichts aller Switches und Plattformunterschiede wahrscheinlich eine vergebliche Anstrengung ist.
l0b0
related: stackoverflow.com/questions/29613304/…
Ciro Santilli am

Antworten:

6

Wenn Sie die Potenz regulärer Ausdrücke nicht benötigen, verwenden Sie sie nicht. Das ist gut.
Dies ist jedoch kein regulärer Ausdruck .

sed 's|literal_pattern|replacement_string|g'

Also, wenn /es Ihr Problem ist, verwenden |Sie und Sie müssen nicht ersteren entkommen.

ps: zu den Kommentaren siehe auch diese Stackoverflow-Antwort auf Escape a string for sed search pattern .


Update: Wenn Sie sich gut mit Perl versuchen Sie es mit \Qund \Ewie diese,
perl -pe 's|\Qliteral_pattern\E|replacement_string|g'
RedGrittyBrickhat auch vorgeschlagen , einen ähnlichen Trick mit stärkerer Perl - Syntax in einem Kommentar hier

nik
quelle
Vielen Dank, ich wusste nichts über den Unterschied zwischen / und |
Andrea
64
Ich bin mir nicht sicher, ob diese Antwort nützlich ist ... Der einzige Unterschied zwischen s|||und s///besteht darin, dass das Trennzeichen unterschiedlich ist und ein Zeichen nicht maskiert werden muss. Das könnte man auch tun s###. Das eigentliche Problem hierbei ist, dass sich das OP nicht darum kümmern muss, dem Inhalt von zu entkommen literal_pattern(was überhaupt nicht wörtlich ist und als regulärer Ausdruck interpretiert wird).
Benj
15
Dies wird die Interpretation anderer Sonderzeichen nicht vermeiden. Was passiert , wenn Suche nach 1234.*aaaes mit Ihrer Lösung viel mehr als die beabsichtigten entsprechen 1234\.\*aaa.
Matteo
20
Diese Antwort sollte nicht akzeptiert werden
Steven Lu
2
Das geht völlig daneben. Der zuzuordnende Text kann eine beliebige Seltsamkeit enthalten. In meinem Fall ist es ein zufälliges Passwort. Sie wissen, wie die gehen
Christian Bongiorno
13
export FIND='find this'
export REPLACE='replace with this'
ruby -p -i -e "gsub(ENV['FIND'], ENV['REPLACE'])" path/to/file

Dies ist die einzige 100% sichere Lösung, weil:

  • Es ist eine statische Substition, keine reguläre Ausdrucksweise, keine Notwendigkeit, irgendetwas zu entkommen (also der Verwendung überlegen sed).
  • Es wird nicht kaputt gehen, wenn Ihr String }char enthält (also einer eingereichten Perl-Lösung überlegen)
  • Es wird nicht mit einem Zeichen brechen, weil ENV['FIND']verwendet wird, nicht $FIND. Mit $FINDoder Ihrem Text in Ruby-Code können Sie einen Syntaxfehler feststellen, wenn Ihre Zeichenfolge einen ungeschützten Text enthält '.
Nowaker
quelle
Ich musste export FIND='find this; export REPLACE='replace with this';in meinem Bash-Skript damit umgehen ENV['FIND']und ENV['replace']hatte die erwarteten Werte. Ich habe einige wirklich lange verschlüsselte Zeichenfolgen in einer Datei ersetzt. Dies war nur das Ticket.
DMfll
Dies ist eine gute Antwort, da es zuverlässig ist und Ruby allgegenwärtig ist. Basierend auf dieser Antwort verwende ich jetzt dieses Shell-Skript .
Lövborg
Funktioniert leider nicht, wenn FIND mehrere Zeilen enthält.
Adrelanos
Nichts hindert es daran, mit mehreren Zeilen in FIND zu arbeiten. Verwenden Sie doppelte Anführungszeichen \ n.
Nowaker
7

Der replaceBefehl wird dies tun.

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

Ortswechsel:

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

Zu stdout:

replace text replacement < path/to/the/file

Beispiel:

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

Der replaceBefehl kommt mit MySQL oder MariaDB.

Derek Veit
quelle
3
Beachten Sie, dass das Ersetzen veraltet ist und möglicherweise in Zukunft nicht mehr verfügbar ist
Rogelio
1
Warum um alles in der Welt kommt ein solcher Grundbefehl mit einer Datenbank?
Masterxilo
3
@masterxilo Eine bessere Frage könnte sein - warum kommt ein solcher Grundbefehl bei modernen Betriebssystemen nicht vor? ;-)
Mark Thomson
3

Sie könnten auch in Perl verwenden \QMechanismus „ Zitat (deaktivieren) Muster Metazeichen

perl -pe 'BEGIN {$text = q{your */text/?goes"here"}} s/\Q$text\E/replacement/g'
Glenn Jackman
quelle
3
Oderperl -pe 's(\Qyour */text/?goes"here")(replacement)' file
RedGrittyBrick
3

Schau dir mein Perl-Skript an. Es kann genau das tun, was Sie benötigen, ohne den regulären Ausdruck implizit oder explizit zu verwenden:

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

sehr praktisch, oder? Ich musste Perl lernen, um es zu tun. weil ich es wirklich wirklich brauche.

Samer Ata
quelle
2

Sie können dies tun, indem Sie Ihren Mustern entkommen . So was:

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'

Credits für diese Lösung finden Sie hier: https://stackoverflow.com/questions/407523/escape-a-string-for-a-sed-replace-pattern

Hinweis 1: Dies funktioniert nur für nicht leere Schlüsselwörter. Leere Keywords werden von sed nicht akzeptiert (sed -e 's//replacement/' ) .

Hinweis 2: Leider kenne ich kein beliebtes Tool, das KEINE regulären Ausdrücke verwenden würde, um das Problem zu lösen. Sie können ein solches Tool in Rust oder C schreiben, es ist jedoch nicht standardmäßig vorhanden.

VasyaNovikov
quelle
Dies verfehlt völlig den OP-Punkt. Natürlich können Sie dem Muster entkommen, aber für einige Muster ist dies mühsam.
Eisschwert
@icecreamsword hast du meine Antwort unter der ersten Zeile gelesen? Das Skript wird automatisch ausgeblendet .
VasyaNovikov
1

Ich habe ein paar andere Antworten zusammengestellt und mir Folgendes ausgedacht:

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" "$@"
}
Xiong Chiamiov
quelle
Funktioniert nicht mit Newlines. Auch hilft nicht, Newlines mit zu entkommen \n. Irgendeine Lösung?
Adrelanos
1

Sie können phps str_replace verwenden :

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

Hinweis: Sie müssten jedoch weiterhin einfache 'und doppelte Anführungszeichen "umgehen.

simlev
quelle
0

Node.JS-Äquivalent zu @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;});});'
BEIM
quelle
0

Hier ist noch eine "fast" Arbeitsweise.

Benutze vi oder vim.

Erstellen Sie eine Textdatei mit Ihrer Ersetzung:

:% sno / my search string \\ "-: # 2; g ('. j'); \\"> / my replacestring = \\ "bac) (o: # 46; \\"> /
:X

dann führe vi oder vim von der Kommandozeile aus:

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

:% sno ist der Befehl vi zum Suchen und Ersetzen ohne Zauberei.

Ich habe das Trennzeichen gewählt.

: x speichert und beendet vi.

Sie müssen Backslashes (umgekehrte Schrägstriche) ausschließen. '\' Der Schrägstrich '/' kann zB durch ein Fragezeichen '?' Ersetzt werden. oder etwas anderes, das nicht in Ihrer Such- oder Ersetzungszeichenfolge enthalten ist, Pipe '|' hat bei mir aber nicht funktioniert.

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

Samuel Åslund
quelle