Wie kann ich eine Zeichenfolge in einer oder mehreren Dateien ersetzen?

752

Das Ersetzen von Zeichenfolgen in Dateien anhand bestimmter Suchkriterien ist eine sehr häufige Aufgabe. Wie kann ich

  • String ersetzt foomit barin allen Dateien im aktuellen Verzeichnis?
  • Dasselbe rekursiv für Unterverzeichnisse?
  • Nur ersetzen, wenn der Dateiname mit einer anderen Zeichenfolge übereinstimmt?
  • Nur ersetzen, wenn die Zeichenfolge in einem bestimmten Kontext gefunden wird?
  • Ersetzen, wenn sich die Zeichenfolge in einer bestimmten Zeilennummer befindet?
  • Ersetzen Sie mehrere Zeichenfolgen durch denselben Ersatz
  • Ersetzen Sie mehrere Zeichenfolgen durch unterschiedliche Ersetzungen
terdon
quelle
2
Dies soll eine kanonische Frage und Antwort zu diesem Thema sein (siehe diese Metadiskussion ). Bitte editieren Sie meine Antwort unten oder fügen Sie Ihre eigene hinzu.
Terdon

Antworten:

1010

1. Ersetzen aller Vorkommen einer Zeichenfolge durch eine andere in allen Dateien im aktuellen Verzeichnis:

In diesen Fällen wissen Sie , dass das Verzeichnis nur reguläre Dateien enthält und dass Sie alle nicht ausgeblendeten Dateien verarbeiten möchten. Ist dies nicht der Fall, verwenden Sie die Ansätze in 2.

Alle sedLösungen in dieser Antwort gehen von GNU aus sed. Wenn Sie FreeBSD oder OS / X verwenden, ersetzen Sie -idurch -i ''. Beachten Sie außerdem, dass die Verwendung des -iSwitches mit einer beliebigen Version von sedbestimmte Auswirkungen auf die Sicherheit des Dateisystems hat und in keinem Skript empfohlen wird, das Sie auf irgendeine Weise verteilen möchten.

  • Nicht rekursiv, nur Dateien in diesem Verzeichnis:

    sed -i -- 's/foo/bar/g' *
    perl -i -pe 's/foo/bar/g' ./* 

    (Der perlFehler tritt bei Dateinamen auf, die auf |oder mit Leerzeichen enden ).

  • Rekursive, reguläre Dateien ( einschließlich versteckter ) in diesem und allen Unterverzeichnissen

    find . -type f -exec sed -i 's/foo/bar/g' {} +

    Wenn Sie zsh verwenden:

    sed -i -- 's/foo/bar/g' **/*(D.)

    (Kann fehlschlagen, wenn die Liste zu groß ist. Sehen Sie zargs, um das Problem zu umgehen .)

    Bash kann nicht direkt nach regulären Dateien suchen, es wird eine Schleife benötigt (geschweifte Klammern vermeiden es, die Optionen global zu setzen):

    ( shopt -s globstar dotglob;
        for file in **; do
            if [[ -f $file ]] && [[ -w $file ]]; then
                sed -i -- 's/foo/bar/g' "$file"
            fi
        done
    )

    Die Dateien werden ausgewählt, wenn es sich um tatsächliche Dateien handelt (-f) und beschreibbar sind (-w).

2. Ersetzen Sie nur, wenn der Dateiname mit einer anderen Zeichenfolge übereinstimmt / eine bestimmte Erweiterung hat / von einem bestimmten Typ ist usw.:

  • Nicht rekursive Dateien nur in diesem Verzeichnis:

    sed -i -- 's/foo/bar/g' *baz*    ## all files whose name contains baz
    sed -i -- 's/foo/bar/g' *.baz    ## files ending in .baz
  • Rekursive, reguläre Dateien in diesem und allen Unterverzeichnissen

    find . -type f -name "*baz*" -exec sed -i 's/foo/bar/g' {} +

    Wenn Sie bash verwenden (geschweifte Klammern vermeiden es, die Optionen global festzulegen):

    ( shopt -s globstar dotglob
        sed -i -- 's/foo/bar/g' **baz*
        sed -i -- 's/foo/bar/g' **.baz
    )

    Wenn Sie zsh verwenden:

    sed -i -- 's/foo/bar/g' **/*baz*(D.)
    sed -i -- 's/foo/bar/g' **/*.baz(D.)

    Das --dient dazu mitzuteilen, seddass in der Kommandozeile keine Flags mehr vergeben werden. Dies ist nützlich, um vor Dateinamen zu schützen, die mit beginnen -.

  • Wenn eine Datei von einem bestimmten Typ ist, z. B. ausführbar (siehe man findfür weitere Optionen):

    find . -type f -executable -exec sed -i 's/foo/bar/g' {} +

    zsh:

    sed -i -- 's/foo/bar/g' **/*(D*)

3. Ersetzen Sie nur, wenn die Zeichenfolge in einem bestimmten Kontext gefunden wird

  • Ersetzen Sie foomit barnur, wenn es bazspäter in derselben Zeile gibt:

    sed -i 's/foo\(.*baz\)/bar\1/' file

    In sed, mit \( \)spart , was in den Klammern ist und Sie können es dann Zugriff mit \1. Es gibt viele Variationen dieses Themas, um mehr über solche regulären Ausdrücke zu erfahren, siehe hier .

  • Nur ersetzen foodurch bar, wenn fooin der 3d-Spalte (Feld) der Eingabedatei gefunden wird (unter der Annahme von durch Leerzeichen getrennten Feldern):

    gawk -i inplace '{gsub(/foo/,"baz",$3); print}' file

    (benötigt gawk4.1.0 oder neuer).

  • Verwenden Sie für ein anderes Feld einfach, $Nwo Ndie Nummer des gewünschten Feldes ist. :Verwenden Sie für ein anderes Feldtrennzeichen ( in diesem Beispiel):

    gawk -i inplace -F':' '{gsub(/foo/,"baz",$3);print}' file

    Eine andere Lösung mit perl:

    perl -i -ane '$F[2]=~s/foo/baz/g; $" = " "; print "@F\n"' foo 

    HINWEIS: Sowohl die awkals auch die perlLösungen wirken sich auf den Abstand in der Datei aus (entfernen Sie die führenden und nachfolgenden Leerzeichen und konvertieren Sie die Folgen von Leerzeichen in ein Leerzeichen in den übereinstimmenden Zeilen). Verwenden Sie für ein anderes Feld, $F[N-1]wo Ndie gewünschte Feldnummer ist, und für ein anderes Feld das Trennzeichen (das $"=":"setzt das Ausgabefeldtrennzeichen auf :):

    perl -i -F':' -ane '$F[2]=~s/foo/baz/g; $"=":";print "@F"' foo 
  • Ersetzen Sie foomit barnur in der 4. Zeile:

    sed -i '4s/foo/bar/g' file
    gawk -i inplace 'NR==4{gsub(/foo/,"baz")};1' file
    perl -i -pe 's/foo/bar/g if $.==4' file

4. Mehrere Ersetzungsoperationen: Ersetzen durch verschiedene Zeichenfolgen

  • Sie können sedBefehle kombinieren :

    sed -i 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file

    Beachten Sie, dass , um Angelegenheiten ( sed 's/foo/bar/g; s/bar/baz/g'wird ersetzt foomit baz).

  • oder Perl-Befehle

    perl -i -pe 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file
  • Wenn Sie über eine große Anzahl von Mustern verfügen, ist es einfacher, Ihre Muster und ihre Ersetzungen in einer sedSkriptdatei zu speichern:

    #! /usr/bin/sed -f
    s/foo/bar/g
    s/baz/zab/g
  • Wenn Sie zu viele Musterpaare haben, als dass dies möglich wäre, können Sie Musterpaare aus einer Datei lesen (zwei durch Leerzeichen getrennte Muster, $ pattern und $ replacement pro Zeile):

    while read -r pattern replacement; do   
        sed -i "s/$pattern/$replacement/" file
    done < patterns.txt
  • Bei langen Listen mit Mustern und großen Datendateien ist dies recht langsam. Sie können also die Muster lesen und sedstattdessen ein Skript daraus erstellen . Im Folgenden wird davon ausgegangen, dass ein <Leerzeichen> eine Liste von MATCH <Leerzeichen> REPLACE- Paaren trennt , die zeilenweise in der Datei vorkommen patterns.txt:

    sed 's| *\([^ ]*\) *\([^ ]*\).*|s/\1/\2/g|' <patterns.txt |
    sed -f- ./editfile >outfile

    Das obige Format ist weitgehend willkürlich und lässt beispielsweise weder in MATCH noch in REPLACE ein <Leerzeichen> zu . Die Methode ist jedoch sehr allgemein: Wenn Sie einen Ausgabestream erstellen können, der wie ein Skript aussieht , können Sie diesen Stream als Skript ausgeben , indem Sie die Skriptdatei von stdin angeben.sedsedsed-

  • Sie können mehrere Skripte auf ähnliche Weise kombinieren und verketten:

    SOME_PIPELINE |
    sed -e'#some expression script'  \
        -f./script_file -f-          \
        -e'#more inline expressions' \
    ./actual_edit_file >./outfile

    Ein POSIX sedverknüpft alle Skripte in der Reihenfolge, in der sie in der Befehlszeile angezeigt werden, zu einem Skript. Keines von diesen muss in einer \newline enden.

  • grep kann auf die gleiche Weise arbeiten:

    sed -e'#generate a pattern list' <in |
    grep -f- ./grepped_file
  • Wenn Sie mit festen Zeichenfolgen als Mustern arbeiten, ist es empfehlenswert, Metazeichen mit regulären Ausdrücken zu umgehen . Sie können dies ziemlich einfach tun:

    sed 's/[]$&^*\./[]/\\&/g
         s| *\([^ ]*\) *\([^ ]*\).*|s/\1/\2/g|
    ' <patterns.txt |
    sed -f- ./editfile >outfile

5. Mehrere Ersetzungsoperationen: Ersetzen Sie mehrere Muster durch dieselbe Zeichenfolge

  • Ersetzen jeder foo, baroder bazmitfoobar

    sed -Ei 's/foo|bar|baz/foobar/g' file
  • oder

    perl -i -pe 's/foo|bar|baz/foobar/g' file
terdon
quelle
2
@Stéphanechazelas danke für die bearbeitung, es wurden ja einige dinge behoben. Bitte entfernen Sie jedoch keine Informationen, die für die Bash relevant sind. Nicht jeder nutzt zsh. Füge auf jeden Fall zshInformationen hinzu, aber es gibt keinen Grund, das Bash-Zeug zu entfernen. Ich weiß auch, dass die Verwendung der Shell für die Textverarbeitung nicht ideal ist, aber es gibt Fälle, in denen dies erforderlich ist. Ich habe eine bessere Version meines ursprünglichen Skripts verwendet, mit der ein sedSkript erstellt wird, anstatt die Shell-Schleife zum Parsen zu verwenden. Dies kann nützlich sein, wenn Sie beispielsweise mehrere hundert Musterpaare haben.
terdon
2
@terdon, deine Bash ist falsch. Bash vor 4.3 folgt beim Abstieg Symlinks. Bash hat auch keine Entsprechung für das (.)Globbing-Qualifikationsmerkmal und kann daher hier nicht verwendet werden. (Sie vermissen einige - auch). Die for-Schleife ist falsch (fehlende -r) und bedeutet, dass mehrere Durchgänge in den Dateien ausgeführt werden und kein Vorteil gegenüber einem sed-Skript entsteht.
Stéphane Chazelas
7
@terdon Was bedeutet --nach sed -iund vor dem Ersatzbefehl?
Geek
5
@Geek, das ist eine POSIX-Sache. Es kennzeichnet das Ende von Optionen und lässt Sie Argumente übergeben, die mit beginnen -. Durch die Verwendung wird sichergestellt, dass die Befehle für Dateien mit Namen wie "" funktionieren -foo. Ohne sie -fwürde das als eine Option analysiert werden.
terdon
1
Seien Sie sehr vorsichtig, wenn Sie einige der rekursiven Befehle in Git-Repositories ausführen. Mit den in Abschnitt 1 dieser Antwort beschriebenen Lösungen werden beispielsweise interne Git-Dateien in einem .gitVerzeichnis geändert und Ihre Kasse tatsächlich durcheinander gebracht. Es ist besser, in bestimmten Verzeichnissen nach Namen zu arbeiten.
Pistos
75

Ein guter r e pl acement Linux - Tool ist RPL , das ursprünglich für das Debian - Projekt geschrieben wurde, so dass es mit verfügbar ist apt-get install rplin jedem Debian abgeleitete Distribution und für andere sein kann, aber ansonsten kann man die Download - tar.gzDatei in SourgeForge .

Einfachstes Anwendungsbeispiel:

 $ rpl old_string new_string test.txt

Beachten Sie, dass die Zeichenfolge in Anführungszeichen gesetzt werden muss, wenn sie Leerzeichen enthält. Achten Sie standardmäßig rplauf Großbuchstaben, aber nicht auf vollständige Wörter . Sie können diese Standardeinstellungen jedoch mit den Optionen -i(Groß- / Kleinschreibung ignorieren) und -w(ganze Wörter) ändern . Sie können auch mehrere Dateien angeben :

 $ rpl -i -w "old string" "new string" test.txt test2.txt

Oder geben Sie die zu durchsuchenden Erweiterungen ( -x) an oder durchsuchen Sie das Verzeichnis sogar rekursiv ( -R):

 $ rpl -x .html -x .txt -R old_string new_string test*

Sie können auch im interaktiven Modus mit der -pOption (Eingabeaufforderung) suchen / ersetzen :

Die Ausgabe zeigt die Anzahl der ersetzten Dateien / Strings und die Art der Suche (Groß- / Kleinschreibung beachten, ganze / teilweise Wörter), kann jedoch mit der Option -q( stiller Modus ) oder noch ausführlicherer Auflistung der Zeilennummern stumm geschaltet werden Übereinstimmungen für jede Datei und jedes Verzeichnis mit der Option -v( ausführlicher Modus ).

Andere Optionen, die es wert sind, in Erinnerung zu bleiben, sind -e(honor e scapes), die es erlauben regular expressions, auch nach Tabulatoren ( \t), neuen Zeilen ( \n) usw. Zu suchen . Auch können Sie verwenden , -fum Berechtigungen zu erzwingen (natürlich nur , wenn die Benutzer Schreibrechte haben) und -ddie Änderung times` zu erhalten).

Wenn Sie sich nicht sicher sind, welche Funktion genau ausgeführt werden soll, verwenden Sie den -s( Simulationsmodus ).

Fran
quelle
2
So viel besser bei der Rückmeldung und Einfachheit als sed. Ich wünschte nur, es wäre erlaubt, auf Dateinamen zu reagieren, und dann wäre es perfekt, wie es ist.
Kzqai
1
Ich mag die -s (Simulationsmodus) :-)
erm3nda
25

So führen Sie ein Suchen und Ersetzen über mehrere Dateien durch :

Sie könnten auch find und sed verwenden, aber ich finde, dass diese kleine Perl-Linie gut funktioniert.

perl -pi -w -e 's/search/replace/g;' *.php
  • -e bedeutet, dass die folgende Codezeile ausgeführt wird.
  • -i bedeutet Bearbeiten an Ort und Stelle
  • -w Warnungen schreiben
  • -p Durchläuft die Eingabedatei und druckt jede Zeile, nachdem das Skript darauf angewendet wurde.

Meine besten Ergebnisse kommen von der Verwendung von Perl und Grep (um sicherzustellen, dass die Datei den Suchausdruck hat)

perl -pi -w -e 's/search/replace/g;' $( grep -rl 'search' )
Alejandro Salamanca Mazuelo
quelle
13

Sie können Vim im Ex-Modus verwenden:

Zeichenfolge ALF durch BRA in allen Dateien im aktuellen Verzeichnis ersetzen?

for CHA in *
do
  ex -sc '%s/ALF/BRA/g' -cx "$CHA"
done

Dasselbe rekursiv für Unterverzeichnisse?

find -type f -exec ex -sc '%s/ALF/BRA/g' -cx {} ';'

Nur ersetzen, wenn der Dateiname mit einer anderen Zeichenfolge übereinstimmt?

for CHA in *.txt
do
  ex -sc '%s/ALF/BRA/g' -cx "$CHA"
done

Nur ersetzen, wenn die Zeichenfolge in einem bestimmten Kontext gefunden wird?

ex -sc 'g/DEL/s/ALF/BRA/g' -cx file

Ersetzen, wenn sich die Zeichenfolge in einer bestimmten Zeilennummer befindet?

ex -sc '2s/ALF/BRA/g' -cx file

Ersetzen Sie mehrere Zeichenfolgen durch denselben Ersatz

ex -sc '%s/\vALF|ECH/BRA/g' -cx file

Ersetzen Sie mehrere Zeichenfolgen durch unterschiedliche Ersetzungen

ex -sc '%s/ALF/BRA/g|%s/FOX/GOL/g' -cx file
Steven Penny
quelle
13

Ich habe das benutzt:

grep -r "old_string" -l | tr '\n' ' ' | xargs sed -i 's/old_string/new_string/g'
  1. Listen Sie alle Dateien auf, die enthalten old_string.

  2. Ersetzen Sie newline im Ergebnis durch Leerzeichen (damit die Liste der Dateien eingespeist werden kann) sed.

  3. Führen Sie seddiese Dateien aus, um die alte Zeichenfolge durch eine neue zu ersetzen.

Update: Das obige Ergebnis schlägt bei Dateinamen fehl, die Leerzeichen enthalten. Verwenden Sie stattdessen:

grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g'

o_o_o--
quelle
Beachten Sie, dass dies fehlschlägt, wenn einer Ihrer Dateinamen Leerzeichen, Tabulatoren oder Zeilenumbrüche enthält. Verwendung grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g'wird es mit beliebigen Dateinamen umgehen.
terdon
danke Leute. Update hinzugefügt und den alten Code verlassen, da dies eine interessante Einschränkung ist, die für jemanden nützlich sein kann, der sich dieses Verhaltens nicht bewusst ist.
o_o_o-
6

Aus Benutzersicht ist dies ein schönes und einfaches Unix-Tool, das die Aufgabe perfekt erledigt qsubst. Zum Beispiel,

% qsubst foo bar *.c *.h

ersetzt foomit barin meinen C - Dateien. Ein nettes Feature ist, dass qsubsteine Abfrage ersetzt wird , dh es zeigt mir jedes Vorkommen von foound fragt, ob ich es ersetzen möchte oder nicht. [Sie können bedingungslos (ohne zu fragen) durch die -goOption ersetzen , und es gibt andere Optionen, z. B. -wwenn Sie nur ersetzen möchten, foowenn es sich um ein ganzes Wort handelt.]

Wie man es bekommt: qsubstWurde von der Mouse (von McGill) erfunden und im August 1987 auf comp.unix.sources 11 (7) gepostet . Aktualisierte Versionen existieren. Zum Beispiel qsubst.c,v 1.8 2004/11/01kompiliert und läuft die NetBSD-Version perfekt auf meinem Mac.

phs
quelle
2

Ich brauchte etwas , das eine trockenlauf Option bieten würde und rekursiv mit einem glob arbeiten, und nach dem Versuch , es zu tun mit awkund sedich aufgab und stattdessen tat es in Python.

Das Skript durchsucht rekursiv alle Dateien, die einem Glob-Muster entsprechen (z. B. --glob="*.html"), nach einem regulären Ausdruck und ersetzt ihn durch den regulären Ausdruck:

find_replace.py [--dir=my_folder] \
    --search-regex=<search_regex> \
    --replace-regex=<replace_regex> \
    --glob=[glob_pattern] \
    --dry-run

Jede lange Option wie --search-regexhat eine entsprechende kurze Option, dh -s. Führen Sie mit aus -h, um alle Optionen anzuzeigen.

So werden beispielsweise alle Daten von 2017-12-31nach gekippt 31-12-2017:

python replace.py --glob=myfile.txt \
    --search-regex="(\d{4})-(\d{2})-(\d{2})" \
    --replace-regex="\3-\2-\1" \
    --dry-run --verbose
import os
import fnmatch
import sys
import shutil
import re

import argparse

def find_replace(cfg):
    search_pattern = re.compile(cfg.search_regex)

    if cfg.dry_run:
        print('THIS IS A DRY RUN -- NO FILES WILL BE CHANGED!')

    for path, dirs, files in os.walk(os.path.abspath(cfg.dir)):
        for filename in fnmatch.filter(files, cfg.glob):

            if cfg.print_parent_folder:
                pardir = os.path.normpath(os.path.join(path, '..'))
                pardir = os.path.split(pardir)[-1]
                print('[%s]' % pardir)
            filepath = os.path.join(path, filename)

            # backup original file
            if cfg.create_backup:
                backup_path = filepath + '.bak'

                while os.path.exists(backup_path):
                    backup_path += '.bak'
                print('DBG: creating backup', backup_path)
                shutil.copyfile(filepath, backup_path)

            with open(filepath) as f:
                old_text = f.read()

            all_matches = search_pattern.findall(old_text)

            if all_matches:

                print('Found {} matches in file {}'.format(len(all_matches), filename))

                new_text = search_pattern.sub(cfg.replace_regex, old_text)

                if not cfg.dry_run:
                    with open(filepath, "w") as f:
                        print('DBG: replacing in file', filepath)
                        f.write(new_text)
                else:
                    for idx, matches in enumerate(all_matches):
                        print("Match #{}: {}".format(idx, matches))

                    print("NEW TEXT:\n{}".format(new_text))

            elif cfg.verbose:
                print('File {} does not contain search regex "{}"'.format(filename, cfg.search_regex))


if __name__ == '__main__':

    parser = argparse.ArgumentParser(description='''DESCRIPTION:
    Find and replace recursively from the given folder using regular expressions''',
                                     formatter_class=argparse.RawDescriptionHelpFormatter,
                                     epilog='''USAGE:
    {0} -d [my_folder] -s <search_regex> -r <replace_regex> -g [glob_pattern]

    '''.format(os.path.basename(sys.argv[0])))

    parser.add_argument('--dir', '-d',
                        help='folder to search in; by default current folder',
                        default='.')

    parser.add_argument('--search-regex', '-s',
                        help='search regex',
                        required=True)

    parser.add_argument('--replace-regex', '-r',
                        help='replacement regex',
                        required=True)

    parser.add_argument('--glob', '-g',
                        help='glob pattern, i.e. *.html',
                        default="*.*")

    parser.add_argument('--dry-run', '-dr',
                        action='store_true',
                        help="don't replace anything just show what is going to be done",
                        default=False)

    parser.add_argument('--create-backup', '-b',
                        action='store_true',
                        help='Create backup files',
                        default=False)

    parser.add_argument('--verbose', '-v',
                        action='store_true',
                        help="Show files which don't match the search regex",
                        default=False)

    parser.add_argument('--print-parent-folder', '-p',
                        action='store_true',
                        help="Show the parent info for debug",
                        default=False)

    config = parser.parse_args(sys.argv[1:])

    find_replace(config)

Here ist eine aktualisierte Version des Skripts, die die Suchbegriffe und Ersetzungen in verschiedenen Farben hervorhebt.

ccpizza
quelle
1
Ich verstehe nicht, warum Sie etwas so komplex machen würden. Verwenden Sie für die Rekursion entweder die globstarOption bash (oder die entsprechende Option Ihrer Shell) und **globs oder find. Für einen Trockenlauf verwenden Sie einfach sed. Sofern Sie diese -iOption nicht verwenden, werden keine Änderungen vorgenommen. Für ein Backup verwenden Sie sed -i.bak(oder perl -i .bak); Verwenden Sie für Dateien, die nicht übereinstimmen grep PATTERN file || echo file. Und warum in aller Welt sollte Python den Glob erweitern, anstatt die Shell dies tun zu lassen? Warum script.py --glob=foo*statt nur script.py foo*?
Terdon
1
Mein Grund ist sehr einfach: (1) vor allem das einfache Debuggen; (2) nur ein einziges gut dokumentiertes Tool mit einer unterstützenden Community verwenden (3) nicht wissen sedund awkgut und nicht bereit sein, zusätzliche Zeit für die Beherrschung zu investieren, (4) Lesbarkeit, (5) diese Lösung funktioniert auch auf nicht-posix-Systemen (nicht, dass ich das brauche, aber jemand anderes könnte).
ccpizza
1

ripgrep ( befehlsnamerg ) ist ein greptool, unterstützt aber auch suchen und ersetzen.

$ cat ip.txt
dark blue and light blue
light orange
blue sky
$ # by default, line number is displayed if output destination is stdout
$ # by default, only lines that matched the given pattern is displayed
$ # 'blue' is search pattern and -r 'red' is replacement string
$ rg 'blue' -r 'red' ip.txt
1:dark red and light red
3:red sky

$ # --passthru option is useful to print all lines, whether or not it matched
$ # -N will disable line number prefix
$ # this command is similar to: sed 's/blue/red/g' ip.txt
$ rg --passthru -N 'blue' -r 'red' ip.txt
dark red and light red
light orange
red sky


rg unterstützt keine In-Place-Option, daher müssen Sie dies selbst tun

$ # -N isn't needed here as output destination is a file
$ rg --passthru 'blue' -r 'red' ip.txt > tmp.txt && mv tmp.txt ip.txt
$ cat ip.txt
dark red and light red
light orange
red sky


Siehe Rust regex Dokumentation für Syntax für reguläre Ausdrücke und Funktionen. Der -PSchalter aktiviert die PCRE2-Version . rgunterstützt standardmäßig Unicode.

$ # non-greedy quantifier is supported
$ echo 'food land bark sand band cue combat' | rg 'foo.*?ba' -r 'X'
Xrk sand band cue combat

$ # unicode support
$ echo 'fox:αλεπού,eagle:αετός' | rg '\p{L}+' -r '($0)'
(fox):(αλεπού),(eagle):(αετός)

$ # set operator example, remove all punctuation characters except . ! and ?
$ para='"Hi", there! How *are* you? All fine here.'
$ echo "$para" | rg '[[:punct:]--[.!?]]+' -r ''
Hi there! How are you? All fine here.

$ # use -P if you need even more advanced features
$ echo 'car bat cod map' | rg -P '(bat|map)(*SKIP)(*F)|\w+' -r '[$0]'
[car] bat [cod] map


Mit grepdieser -FOption können feste Zeichenfolgen abgeglichen werden, eine praktische Option, die meines sedErachtens auch implementiert werden sollte.

$ printf '2.3/[4]*6\nfoo\n5.3-[4]*9\n' | rg --passthru -F '[4]*' -r '2'
2.3/26
foo
5.3-29


Eine weitere praktische Option ist -Udie Möglichkeit des mehrzeiligen Abgleichs

$ # (?s) flag will allow . to match newline characters as well
$ printf '42\nHi there\nHave a Nice Day' | rg --passthru -U '(?s)the.*ice' -r ''
42
Hi  Day


rg Kann auch Dateien im DOS-Stil verarbeiten

$ # same as: sed -E 's/\w+(\r?)$/123\1/'
$ printf 'hi there\r\ngood day\r\n' | rg --passthru --crlf '\w+$' -r '123'
hi 123
good 123


Ein weiterer Vorteil von rgist, dass es wahrscheinlich schneller ist alssed

$ # for small files, initial processing time of rg is a large component
$ time echo 'aba' | sed 's/a/b/g' > f1
real    0m0.002s
$ time echo 'aba' | rg --passthru 'a' -r 'b' > f2
real    0m0.007s

$ # for larger files, rg is likely to be faster
$ # 6.2M sample ASCII file
$ wget https://norvig.com/big.txt    
$ time LC_ALL=C sed 's/\bcat\b/dog/g' big.txt > f1
real    0m0.060s
$ time rg --passthru '\bcat\b' -r 'dog' big.txt > f2
real    0m0.048s
$ diff -s f1 f2
Files f1 and f2 are identical

$ time LC_ALL=C sed -E 's/\b(\w+)(\s+\1)+\b/\1/g' big.txt > f1
real    0m0.725s
$ time rg --no-pcre2-unicode --passthru -wP '(\w+)(\s+\1)+' -r '$1' big.txt > f2
real    0m0.093s
$ diff -s f1 f2
Files f1 and f2 are identical
Sundeep
quelle