Jak mogę zamienić ciąg w pliku (plikach)?

751

Zastępowanie ciągów w plikach na podstawie określonych kryteriów wyszukiwania jest bardzo częstym zadaniem. Jak mogę

  • zastąpić ciąg fooze barwszystkich plików w bieżącym katalogu?
  • zrobić to samo rekurencyjnie dla podkatalogów?
  • zamieniać tylko, jeśli nazwa pliku pasuje do innego ciągu?
  • zamieniać tylko, jeśli ciąg znajduje się w określonym kontekście?
  • zastąpić, jeśli ciąg jest na określonym numerze linii?
  • zamień wiele ciągów z tym samym zamiennikiem
  • zamień wiele ciągów z różnymi zamiennikami
terdon
źródło
2
To ma być kanoniczne pytania i odpowiedzi na ten temat (patrz ta meta dyskusja ), proszę edytować moją odpowiedź poniżej lub dodać własną.
terdon

Odpowiedzi:

1009

1. Zastępowanie wszystkich wystąpień jednego ciągu innym ciągiem we wszystkich plikach w bieżącym katalogu:

Są to przypadki, w których wiesz, że katalog zawiera tylko zwykłe pliki i że chcesz przetwarzać wszystkie pliki nie ukryte. Jeśli tak nie jest, zastosuj metody opisane w 2.

Wszystkie sedrozwiązania zawarte w tej odpowiedzi zakładają GNU sed. W przypadku korzystania z FreeBSD lub OS / X, wymienić -isię -i ''. Należy również pamiętać, że użycie -iprzełącznika w dowolnej wersji sedma pewne implikacje dla bezpieczeństwa systemu plików i jest niewskazane w żadnym skrypcie, który planuje się w jakikolwiek sposób rozpowszechniać.

  • Nie rekurencyjne, tylko pliki w tym katalogu:

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

    ( perljeden nie powiedzie się w przypadku nazw plików kończących się na |spację ).

  • Rekurencyjne, regularne pliki (w tym ukryte ) w tym i we wszystkich podkatalogach

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

    Jeśli używasz zsh:

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

    (może się nie powieść, jeśli lista jest zbyt duża, patrz zargsobejść).

    Bash nie może sprawdzić bezpośrednio zwykłych plików, potrzebna jest pętla (nawiasy klamrowe unikają globalnego ustawiania opcji):

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

    Pliki są wybierane, gdy są rzeczywistymi plikami (-f) i można je zapisywać (-w).

2. Zastąp tylko, jeśli nazwa pliku pasuje do innego ciągu / ma określone rozszerzenie / jest określonego typu itp .:

  • Pliki nierekurencyjne tylko w tym katalogu:

    sed -i -- 's/foo/bar/g' *baz*    ## all files whose name contains baz
    sed -i -- 's/foo/bar/g' *.baz    ## files ending in .baz
    
  • Rekurencyjne, regularne pliki w tym i we wszystkich podkatalogach

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

    Jeśli używasz bash (nawiasy klamrowe unikaj ustawiania opcji globalnie):

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

    Jeśli używasz zsh:

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

    Do --służy powiedzieć sed, że nie więcej flagi zostaną podane w wierszu poleceń. Jest to przydatne do ochrony przed nazwami plików rozpoczynającymi się od -.

  • Jeśli plik jest określonego typu, na przykład wykonywalny (zobacz man findwięcej opcji):

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

    zsh:

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

3. Zastąp tylko, jeśli ciąg zostanie znaleziony w określonym kontekście

  • Wymień foosię bartylko wtedy, gdy znajduje się bazpóźniej na tej samej linii:

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

    Za sedpomocą \( \)zapisuje wszystko, co jest w nawiasach, a następnie możesz uzyskać do niego dostęp za pomocą \1. Istnieje wiele odmian tego tematu, aby dowiedzieć się więcej o takich wyrażeniach regularnych, zobacz tutaj .

  • Wymienić fooz bartylko wtedy, gdy fooznajduje się na kolumnie 3d (pole) pliku (przy założeniu pola Oddzielona spacjami)

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

    (wymaga wersji gawk4.1.0 lub nowszej).

  • W przypadku innego pola wystarczy użyć $Ngdzie Njest numerem pola zainteresowania. W przypadku innego separatora pól ( :w tym przykładzie) użyj:

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

    Inne rozwiązanie wykorzystujące perl:

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

    UWAGA: zarówno rozwiązania, jak awki perlwpłyną na odstępy w pliku (usuń początkowe i końcowe spacje oraz przekonwertuj sekwencje spacji na jeden znak spacji w pasujących liniach). Dla innego pola użyj $F[N-1]gdzie Njest żądany numer pola, a dla innego separatora pól ( $"=":"ustawia separator pól wyjściowych na :):

    perl -i -F':' -ane '$F[2]=~s/foo/baz/g; $"=":";print "@F"' foo 
  • Wymień foosię bartylko na 4 linii:

    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. Wiele operacji zamiany: zamień na inne ciągi

  • Możesz łączyć sedpolecenia:

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

    Pamiętaj, że zamówienie ma znaczenie ( sed 's/foo/bar/g; s/bar/baz/g'zostanie zastąpione fooprzez baz).

  • lub Perl

    perl -i -pe 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file
  • Jeśli masz dużą liczbę wzorców, łatwiej jest zapisać wzorce i ich zamienniki w sedpliku skryptu:

    #! /usr/bin/sed -f
    s/foo/bar/g
    s/baz/zab/g
    
  • Lub, jeśli masz zbyt wiele par wzorów, aby powyższe było możliwe, możesz odczytać pary wzorów z pliku (dwa wzorce oddzielone spacjami, wzór $ i zastąpienie $, w wierszu):

    while read -r pattern replacement; do   
        sed -i "s/$pattern/$replacement/" file
    done < patterns.txt
    
  • To będzie dość powolne w przypadku długich list wzorców i dużych plików danych, więc możesz chcieć przeczytać wzorce i utworzyć sedz nich skrypt. Poniżej założono, że separator <space> oddziela listę par MATCH <space> REPLACE występujących jeden w wierszu w pliku patterns.txt:

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

    Powyższy format jest w dużej mierze arbitralny i, na przykład, nie pozwala na użycie <spacji> w MATCH lub REPLACE . Metoda jest jednak bardzo ogólna: w zasadzie, jeśli możesz utworzyć strumień wyjściowy, który wygląda jak sedskrypt, możesz go pobrać jako sedskrypt, określając sedplik skryptu jako -standard.

  • Możesz łączyć i łączyć wiele skryptów w podobny sposób:

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

    POSIX sedpołączy wszystkie skrypty w jeden w kolejności, w jakiej pojawiają się w wierszu poleceń. Żadna z tych nie musi kończyć się \newline.

  • grep może działać w ten sam sposób:

    sed -e'#generate a pattern list' <in |
    grep -f- ./grepped_file
    
  • Podczas pracy z ciągami stałymi jako wzorcami dobrą praktyką jest unikanie metaznaków wyrażeń regularnych . Możesz to zrobić dość łatwo:

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

5. Wiele operacji zamiany: zastąp wiele wzorców tym samym łańcuchem

  • Zastąpić dowolnego foo, barlub bazzfoobar

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

    perl -i -pe 's/foo|bar|baz/foobar/g' file
terdon
źródło
2
@ StéphaneChazelas dzięki za edycję, naprawiono kilka rzeczy. Nie usuwaj jednak informacji istotnych dla bash. Nie wszyscy używają zsh. Jak najbardziej dodaj zshinformacje, ale nie ma powodu, aby usuwać bash. Wiem też, że używanie powłoki do przetwarzania tekstu nie jest idealne, ale są przypadki, w których jest to potrzebne. Zedytowałem w lepszej wersji mojego oryginalnego skryptu, który utworzy sedskrypt zamiast używać pętli powłoki do analizowania. Może to być przydatne, jeśli masz na przykład kilkaset par wzorów.
terdon
2
@terdon, twój bash jest niepoprawny. bash przed 4.3 będzie podążał za dowiązaniami symbolicznymi podczas schodzenia. Również bash nie ma odpowiednika dla (.)kwalifikatora globbing, więc nie można go tutaj użyć. (również brakuje niektórych). Pętla for jest niepoprawna (brakuje -r) i oznacza wykonanie kilku przejść w plikach i nie daje żadnych korzyści w porównaniu ze skryptem sed.
Stéphane Chazelas
7
@terdon Co oznacza --po sed -ii przed poleceniem zastępczym?
Geek
5
@ Szukaj, to jest POSIX. Oznacza koniec opcji i pozwala przekazywać argumenty zaczynające się od -. Korzystanie z niego gwarantuje, że polecenia będą działać na plikach o nazwach takich jak -foo. Bez tego -fbyłoby analizowane jako opcja.
terdon
1
Zachowaj ostrożność podczas wykonywania niektórych poleceń rekurencyjnych w repozytoriach git. Na przykład rozwiązania przedstawione w sekcji 1 tej odpowiedzi faktycznie zmodyfikują wewnętrzne pliki git w .gitkatalogu i faktycznie popsują proces płatności. Lepiej operować w / w określonych katalogach według nazwy.
Pistos 19.04.16
75

Dobrym r e pl acement narzędzie Linux jest RPL , że dla projektu Debian został pierwotnie napisany, więc jest dostępna apt-get install rplw każdej dystrybucji Debiana pochodzi, i może być dla innych, ale w przeciwnym razie można pobrać tar.gzplik w SourgeForge .

Najprostszy przykład użycia:

 $ rpl old_string new_string test.txt

Zauważ, że jeśli ciąg zawiera spacje, powinien być ujęty w cudzysłów. Domyślnie rplzajmuj się dużymi literami, ale nie pełnymi słowami , ale możesz zmienić te wartości domyślne za pomocą opcji -i(ignoruj ​​wielkość liter) i -w(całe słowa). Możesz także określić wiele plików :

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

Lub nawet określ rozszerzenia ( -x) do przeszukiwania, a nawet przeszukuj rekurencyjnie ( -R) w katalogu:

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

Możesz także wyszukiwać / zastępować w trybie interaktywnym za pomocą opcji -p(monit):

Dane wyjściowe pokazują liczbę zastąpionych plików / ciągów i rodzaj wyszukiwania (wielkość liter / wrażliwe, całe / częściowe słowa), ale może być cichy z opcją -q( tryb cichy ) lub nawet bardziej szczegółowy, zawierając numery wierszy zawierające dopasowania każdego pliku i katalogu z opcją -v( pełny tryb ).

Inne opcje, które warto zapamiętać to -e(honor e scapes), które pozwalają regular expressions, więc możesz przeszukiwać także tabulatory ( \t), nowe linie ( \n) itp. Nawet możesz użyć -fdo wymuszenia uprawnień (oczywiście tylko wtedy, gdy użytkownik ma uprawnienia do zapisu) i -ddo zachowania czasów modyfikacji`).

Wreszcie, jeśli nie masz pewności, co dokładnie dokona, skorzystaj z -s( tryb symulacji ).

Fran
źródło
2
O wiele lepszy w sprzężeniu zwrotnym i prostocie niż sed. Chciałbym tylko, żeby to działało na podstawie nazw plików, a wtedy byłoby idealnie takie, jakie jest.
Kzqai,
1
lubię -s (tryb symulacji) :-)
erm3nda
25

Jak przeprowadzić wyszukiwanie i zastąpić wiele plików, sugeruje:

Możesz także użyć find i sed, ale uważam, że ta mała linia perla działa dobrze.

perl -pi -w -e 's/search/replace/g;' *.php
  • -e oznacza wykonanie następującego wiersza kodu.
  • -i oznacza edycję w miejscu
  • -w pisz ostrzeżenia
  • -p zapętla plik wejściowy, drukując każdą linię po zastosowaniu skryptu.

Moje najlepsze wyniki pochodzą z używania perla i grepa (aby upewnić się, że plik ma wyrażenie wyszukiwania)

perl -pi -w -e 's/search/replace/g;' $( grep -rl 'search' )
Alejandro Salamanca Mazuelo
źródło
13

Możesz używać Vima w trybie Ex:

zamienić ciąg ALF na BRA we wszystkich plikach w bieżącym katalogu?

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

zrobić to samo rekurencyjnie dla podkatalogów?

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

zamieniać tylko, jeśli nazwa pliku pasuje do innego ciągu?

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

zamieniać tylko, jeśli ciąg znajduje się w określonym kontekście?

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

zastąpić, jeśli ciąg jest na określonym numerze linii?

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

zamień wiele ciągów z tym samym zamiennikiem

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

zamień wiele ciągów z różnymi zamiennikami

ex -sc '%s/ALF/BRA/g|%s/FOX/GOL/g' -cx file
Steven Penny
źródło
13

Użyłem tego:

grep -r "old_string" -l | tr '\n' ' ' | xargs sed -i 's/old_string/new_string/g'
  1. Wyświetl wszystkie pliki, które zawierają old_string.

  2. Zamień nowy wiersz w wyniku spacjami (tak, aby można było podać listę plików sed.

  3. Uruchom sedte pliki, aby zastąpić stary ciąg nowym.

Aktualizacja: powyższy wynik nie powiedzie się w przypadku nazw plików zawierających białe spacje. Zamiast tego użyj:

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

o_o_o--
źródło
Pamiętaj, że to się nie powiedzie, jeśli którakolwiek z nazw plików zawiera spacje, tabulatory lub znaki nowej linii. Użycie grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g'sprawi, że poradzi sobie z dowolnymi nazwami plików.
terdon
dzięki chłopaki. dodano aktualizację i pozostawiono stary kod, ponieważ jest to interesujące zastrzeżenie, które może być przydatne dla kogoś, kto nie jest świadomy tego zachowania.
o_o_o--
6

Z punktu widzenia użytkownika, ładne i proste narzędzie uniksowe, które doskonale wykonuje to zadanie qsubst. Na przykład,

% qsubst foo bar *.c *.h

zastąpi foosię barwe wszystkich moich plików C. Miłą cechą jest to, że qsubstzrobi query-replace , czyli pokaże mi każde wystąpienie fooi zapytać, czy chcę, aby zastąpić go lub nie. [Możesz bezwarunkowo zastąpić (bez pytania) -goopcją, a istnieją inne opcje, np. -wJeśli chcesz zastąpić footylko całe słowo.]

Jak go zdobyć: qsubstzostał wynaleziony przez der Mouse (z McGill) i opublikowany na stronie comp.unix.sources 11 (7) w sierpniu 1987 r. Istnieją zaktualizowane wersje. Na przykład wersja NetBSD qsubst.c,v 1.8 2004/11/01kompiluje się i działa idealnie na moim komputerze Mac.

phs
źródło
2

Potrzebowałem czegoś, co zapewniłoby opcję uruchamiania na sucho i działałoby rekurencyjnie z glob, a po próbie zrobienia tego awki sedpoddałem się i zamiast tego zrobiłem to w pythonie.

Skrypt przeszukuje rekurencyjnie wszystkie pliki pasujące do wzorca glob (np --glob="*.html") przez regex i zastępuje z regex zastępczej:

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

Każda długa opcja, taka jak --search-regexma odpowiednią krótką opcję, tj -s. Uruchom z, -haby zobaczyć wszystkie opcje.

Na przykład spowoduje to odwrócenie wszystkich dat od 2017-12-31do 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 to zaktualizowana wersja skryptu, która wyróżnia wyszukiwane hasła i zastępuje je różnymi kolorami.

ccpizza
źródło
1
Nie rozumiem, dlaczego zrobiłbyś coś tak złożonego. Do rekurencji użyj opcji bash (lub odpowiednika twojej powłoki) globstari **globs lub find. Do suchego biegu wystarczy użyć sed. O ile nie skorzystasz z tej -iopcji, nie spowoduje to żadnych zmian. Do tworzenia kopii zapasowych sed -i.bak(lub perl -i .bak); w przypadku plików, które nie pasują, użyj grep PATTERN file || echo file. I dlaczego, u licha, miałbyś python rozwinąć glob zamiast pozwolić powłoce na to? Dlaczego script.py --glob=foo*zamiast tylko script.py foo*?
terdon
1
Moje dlaczego są bardzo proste: (1) przede wszystkim łatwość debugowania; (2) używając tylko jednego dobrze udokumentowanego narzędzia ze wspierającą społecznością (3) nie znając sedi awkdobrze i nie chcąc poświęcać dodatkowego czasu na ich opanowanie, (4) czytelność, (5) to rozwiązanie będzie również działać na systemach nie posix (nie żebym tego potrzebował, ale mógłby ktoś inny).
ccpizza
1

ripgrep (nazwa polecenia rg) jest grepnarzędziem, ale obsługuje również wyszukiwanie i zamianę.

$ 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 nie obsługuje opcji na miejscu, więc musisz to zrobić sam

$ # -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


Zobacz dokumentację wyrażeń regularnych Rust, aby uzyskać składnię i funkcje wyrażeń regularnych. -PPrzełącznik pozwoli PCRE2 smak. rgdomyślnie obsługuje 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


Podobnie grep, -Fopcja pozwoli na dopasowanie ustalonych ciągów, co jest przydatną opcją, którą moim zdaniem sedpowinienem również wdrożyć.

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


Inną przydatną opcją jest -Udopasowanie wieloliniowe

$ # (?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 obsługuje także pliki w stylu dos

$ # 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


Kolejną zaletą rgjest to, że prawdopodobnie będzie szybszy niżsed

$ # 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
źródło