Làm cách nào tôi có thể thay thế một chuỗi trong (các) tệp?


752

Thay thế các chuỗi trong các tệp dựa trên các tiêu chí tìm kiếm nhất định là một nhiệm vụ rất phổ biến. Làm thế nào tôi có thể

  • thay thế chuỗi foobằng bartrong tất cả các tập tin trong thư mục hiện tại?
  • làm đệ quy tương tự cho các thư mục con?
  • chỉ thay thế nếu tên tập tin khớp với chuỗi khác?
  • chỉ thay thế nếu chuỗi được tìm thấy trong một bối cảnh nhất định?
  • thay thế nếu chuỗi nằm trên một số dòng nhất định?
  • thay thế nhiều chuỗi với cùng một sự thay thế
  • thay thế nhiều chuỗi bằng các thay thế khác nhau

2
Đây được dự định là một câu hỏi và trả lời kinh điển về chủ đề này (xem phần thảo luận meta này ), xin vui lòng chỉnh sửa câu trả lời của tôi dưới đây hoặc thêm câu hỏi của riêng bạn.
terdon

Câu trả lời:


1010

1. Thay thế tất cả các lần xuất hiện của một chuỗi bằng một chuỗi khác trong tất cả các tệp trong thư mục hiện tại:

Chúng dành cho các trường hợp bạn biết rằng thư mục chỉ chứa các tệp thông thường và bạn muốn xử lý tất cả các tệp không bị ẩn. Nếu đó không phải là trường hợp, sử dụng các phương pháp trong 2.

Tất cả các sedgiải pháp trong câu trả lời này đều giả định GNU sed. Nếu sử dụng FreeBSD hoặc OS / X, hãy thay thế -ibằng -i ''. Cũng lưu ý rằng việc sử dụng -ichuyển đổi với bất kỳ phiên bản nào sedý nghĩa bảo mật hệ thống tệp nhất định và không thể thực hiện được trong bất kỳ tập lệnh nào mà bạn dự định phân phối theo bất kỳ cách nào.

  • Không đệ quy, các tệp trong thư mục này chỉ:

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

    ( perlmột cái sẽ thất bại cho tên tệp kết thúc bằng |hoặc dấu cách) ).

  • Các tệp đệ quy, thông thường ( bao gồm cả các tệp ẩn ) trong này và tất cả các thư mục con

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

    Nếu bạn đang sử dụng zsh:

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

    (có thể thất bại nếu danh sách quá lớn, xem zargsđể làm việc xung quanh).

    Bash không thể kiểm tra trực tiếp các tệp thông thường, cần một vòng lặp (niềng răng tránh đặt các tùy chọn trên toàn cầu):

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

    Các tệp được chọn khi chúng là các tệp thực tế (-f) và chúng có thể ghi (-w).

2. Chỉ thay thế nếu tên tệp khớp với một chuỗi khác / có phần mở rộng cụ thể / thuộc một loại nhất định, v.v .:

  • Không đệ quy, các tệp trong thư mục này chỉ:

    sed -i -- 's/foo/bar/g' *baz*    ## all files whose name contains baz
    sed -i -- 's/foo/bar/g' *.baz    ## files ending in .baz
  • Các tệp đệ quy, thường xuyên trong này và tất cả các thư mục con

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

    Nếu bạn đang sử dụng bash (niềng răng tránh đặt các tùy chọn trên toàn cầu):

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

    Nếu bạn đang sử dụng zsh:

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

    Các --giao bóng để nói sedrằng không có nhiều cờ sẽ được đưa ra trong các dòng lệnh. Điều này rất hữu ích để bảo vệ chống lại tên tệp bắt đầu bằng -.

  • Nếu một tệp thuộc một loại nhất định, ví dụ, có thể thực thi được (xem man findđể biết thêm tùy chọn):

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

    zsh:

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

3. Chỉ thay thế nếu chuỗi được tìm thấy trong một ngữ cảnh nhất định

  • Thay thế foovới barchỉ nếu có một bazsau trên cùng một dòng:

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

    Trong sed, sử dụng \( \)lưu bất cứ điều gì trong ngoặc đơn và sau đó bạn có thể truy cập nó với \1. Có nhiều biến thể của chủ đề này, để tìm hiểu thêm về các biểu thức thông thường như vậy, xem tại đây .

  • Thay thế foovới barchỉ khi foođược tìm thấy trên các cột 3d (field) của tập tin đầu vào (giả sử các lĩnh vực khoảng trắng phân cách):

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

    (cần gawk4.1.0 hoặc mới hơn).

  • Đối với một lĩnh vực khác nhau chỉ cần sử dụng $Nở đâu Nlà số lĩnh vực quan tâm. Đối với một dấu tách trường khác ( :trong ví dụ này), sử dụng:

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

    Một giải pháp khác sử dụng perl:

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

    LƯU Ý: cả hai awkperlcác giải pháp sẽ ảnh hưởng đến khoảng cách trong tệp (loại bỏ các khoảng trống đầu và cuối, và chuyển đổi các chuỗi khoảng trống thành một ký tự khoảng trắng trong các dòng khớp). Đối với một lĩnh vực khác nhau, sử dụng $F[N-1]ở đâu Nlà số lĩnh vực mà bạn muốn và cho việc sử dụng lĩnh vực phân cách khác nhau ( $"=":"bộ lĩnh vực sản xuất phân cách để :):

    perl -i -F':' -ane '$F[2]=~s/foo/baz/g; $"=":";print "@F"' foo 
  • Thay thế foovới barchỉ trên dòng thứ 4:

    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. Nhiều thao tác thay thế: thay thế bằng các chuỗi khác nhau

  • Bạn có thể kết hợp sedcác lệnh:

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

    Hãy lưu ý rằng vấn đề trật tự ( sed 's/foo/bar/g; s/bar/baz/g'sẽ thay thế foobằng baz).

  • hoặc lệnh Perl

    perl -i -pe 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file
  • Nếu bạn có số lượng mẫu lớn, việc lưu mẫu và thay thế của chúng trong sedtệp tập lệnh sẽ dễ dàng hơn :

    #! /usr/bin/sed -f
    s/foo/bar/g
    s/baz/zab/g
  • Hoặc, nếu bạn có quá nhiều cặp mẫu ở trên là khả thi, bạn có thể đọc các cặp mẫu từ một tệp (hai mẫu được phân tách bằng dấu cách, $ mẫu và $ thay thế, trên mỗi dòng):

    while read -r pattern replacement; do   
        sed -i "s/$pattern/$replacement/" file
    done < patterns.txt
  • Điều đó sẽ khá chậm đối với danh sách dài các mẫu và tệp dữ liệu lớn, do đó bạn có thể muốn đọc các mẫu và tạo sedtập lệnh từ chúng. Sau đây giả sử một dấu phân cách < dấu cách > phân tách một danh sách các cặp MATCH <dấu cách> thay thế xảy ra một dòng trên mỗi dòng trong tệp patterns.txt:

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

    Định dạng trên phần lớn là tùy ý và, ví dụ, không cho phép <dấu cách> trong MATCH hoặc REPLACE . Mặc dù vậy, phương thức này rất chung chung: về cơ bản, nếu bạn có thể tạo một luồng đầu ra trông giống như một sedtập lệnh, thì bạn có thể lấy luồng đó dưới dạng sedtập lệnh bằng cách chỉ định sedtệp tập lệnh là -stdin.

  • Bạn có thể kết hợp và ghép nhiều tập lệnh theo cách tương tự:

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

    Một POSIX sedsẽ ghép tất cả các tập lệnh thành một theo thứ tự chúng xuất hiện trên dòng lệnh. Không ai trong số này cần kết thúc trong một \newline.

  • grep có thể làm việc theo cùng một cách:

    sed -e'#generate a pattern list' <in |
    grep -f- ./grepped_file
  • Khi làm việc với các chuỗi cố định như các mẫu, cách tốt nhất là thoát các siêu ký tự biểu thức chính quy . Bạn có thể làm điều này khá dễ dàng:

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

5. Nhiều thao tác thay thế: thay thế nhiều mẫu bằng cùng một chuỗi

  • Thay thế bất kỳ foo, barhoặc bazbằngfoobar

    sed -Ei 's/foo|bar|baz/foobar/g' file
  • hoặc là

    perl -i -pe 's/foo|bar|baz/foobar/g' file

2
@ StéphaneChazelas cảm ơn vì đã chỉnh sửa, nó thực sự đã sửa một số thứ. Tuy nhiên, vui lòng không xóa thông tin có liên quan đến bash. Không phải ai cũng sử dụng zsh. Bằng mọi cách thêm zshthông tin nhưng không có lý do gì để xóa nội dung bash. Ngoài ra, tôi biết rằng sử dụng shell để xử lý văn bản là không lý tưởng nhưng có những trường hợp cần thiết. Tôi đã chỉnh sửa trong phiên bản tốt hơn của tập lệnh gốc sẽ tạo sedtập lệnh thay vì thực sự sử dụng vòng lặp shell để phân tích cú pháp. Điều này có thể hữu ích nếu bạn có hàng trăm cặp mẫu chẳng hạn.
terdon

2
@terdon, bash của bạn không chính xác. bash trước 4.3 sẽ theo các liên kết tượng trưng khi giảm dần. Ngoài ra bash không có tương đương với (.)vòng loại toàn cầu nên không thể được sử dụng ở đây. (bạn đang thiếu một số - là tốt). Vòng lặp for không chính xác (thiếu -r) và có nghĩa là thực hiện một số lần chuyển trong tệp và không thêm lợi ích nào đối với tập lệnh sed.
Stéphane Chazelas

7
@terdon Điều gì --sau sed -ivà trước lệnh thay thế chỉ ra?
Geek

5
@Geek đó là một điều POSIX. Nó biểu thị sự kết thúc của các tùy chọn và cho phép bạn vượt qua các đối số bắt đầu bằng -. Sử dụng nó đảm bảo rằng các lệnh sẽ hoạt động trên các tệp có tên như -foo. Không có nó, -fsẽ được phân tích cú pháp như là một tùy chọn.
terdon

1
Hãy thật cẩn thận khi thực hiện một số lệnh đệ quy trong kho git. Ví dụ: các giải pháp được cung cấp trong phần 1 của câu trả lời này sẽ thực sự sửa đổi các tệp git nội bộ trong một .gitthư mục và thực sự làm rối thanh toán của bạn. Tốt hơn để hoạt động trong / trên các thư mục cụ thể theo tên.
Pistos

75

Một tốt r e pl acement Linux công cụ là rpl , mà ban đầu được viết cho dự án Debian, vì vậy nó có sẵn với apt-get install rpltrong bất kỳ distro bắt nguồn Debian, và có thể cho người khác, nhưng nếu không bạn có thể tải về các tar.gztập tin trong SourgeForge .

Ví dụ đơn giản nhất về sử dụng:

 $ rpl old_string new_string test.txt

Lưu ý rằng nếu chuỗi chứa khoảng trắng thì nó phải được đặt trong dấu ngoặc kép. Theo mặc định, rplhãy chăm sóc chữ in hoa nhưng không phải là từ hoàn chỉnh , nhưng bạn có thể thay đổi các giá trị mặc định này bằng các tùy chọn -i(trường hợp bỏ qua) và -w(toàn bộ từ). Bạn cũng có thể chỉ định nhiều tệp :

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

Hoặc thậm chí chỉ định các phần mở rộng ( -x) để tìm kiếm hoặc thậm chí tìm kiếm đệ quy ( -R) trong thư mục:

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

Bạn cũng có thể tìm kiếm / thay thế trong chế độ tương tác với -ptùy chọn (prompt):

Đầu ra hiển thị số lượng tệp / chuỗi được thay thế và loại tìm kiếm (trường hợp in / nhạy cảm, toàn bộ / một phần từ), nhưng nó có thể im lặng với tùy chọn -q( chế độ im lặng ) hoặc thậm chí dài hơn, liệt kê các số dòng có chứa phù hợp với từng tệp và thư mục với tùy chọn -v( chế độ dài ).

Các tùy chọn khác đáng ghi nhớ là -e(Honor e scapes) cho phép regular expressions, vì vậy bạn cũng có thể tìm kiếm các tab ( \t), dòng mới ( \n), v.v. Thậm chí bạn có thể sử dụng -fđể buộc các quyền (tất nhiên, chỉ khi người dùng có quyền ghi) và -dđể duy trì thời gian sửa đổi`).

Cuối cùng, nếu bạn không chắc chắn sẽ thực hiện chính xác, hãy sử dụng -s( chế độ mô phỏng ).


2
Vì vậy, tốt hơn nhiều về thông tin phản hồi và đơn giản hơn so với sed. Tôi chỉ muốn nó cho phép hành động theo tên tập tin, và sau đó nó sẽ hoàn hảo như hiện tại.
Kzqai

1
tôi thích -s (chế độ mô phỏng) :-)
erm3nda

25

Cách thực hiện tìm kiếm và thay thế trên nhiều tệp gợi ý:

Bạn cũng có thể sử dụng find và sed, nhưng tôi thấy rằng dòng perl nhỏ này hoạt động độc đáo.

perl -pi -w -e 's/search/replace/g;' *.php
  • -e có nghĩa là thực thi dòng mã sau.
  • -i có nghĩa là chỉnh sửa tại chỗ
  • -w viết cảnh báo
  • -p lặp qua tệp đầu vào, in từng dòng sau khi tập lệnh được áp dụng cho nó.

Kết quả tốt nhất của tôi đến từ việc sử dụng perl và grep (để đảm bảo rằng tệp có biểu thức tìm kiếm)

perl -pi -w -e 's/search/replace/g;' $( grep -rl 'search' )

13

Bạn có thể sử dụng Vim trong chế độ Ex:

thay thế chuỗi ALF bằng BRA trong tất cả các tệp trong thư mục hiện tại?

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

làm đệ quy tương tự cho các thư mục con?

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

chỉ thay thế nếu tên tập tin khớp với chuỗi khác?

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

chỉ thay thế nếu chuỗi được tìm thấy trong một bối cảnh nhất định?

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

thay thế nếu chuỗi nằm trên một số dòng nhất định?

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

thay thế nhiều chuỗi với cùng một sự thay thế

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

thay thế nhiều chuỗi bằng các thay thế khác nhau

ex -sc '%s/ALF/BRA/g|%s/FOX/GOL/g' -cx file

13

Tôi đã sử dụng điều này:

grep -r "old_string" -l | tr '\n' ' ' | xargs sed -i 's/old_string/new_string/g'
  1. Liệt kê tất cả các tập tin có chứa old_string.

  2. Thay thế dòng mới trong kết quả bằng khoảng trắng (để danh sách các tệp có thể được đưa vào sed.

  3. Chạy sedtrên các tệp đó để thay thế chuỗi cũ bằng mới.

Cập nhật: Kết quả trên sẽ thất bại đối với tên tệp có chứa khoảng trắng. Thay vào đó, sử dụng:

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


Lưu ý rằng điều này sẽ thất bại nếu bất kỳ tên tệp nào của bạn chứa khoảng trắng, tab hoặc dòng mới. Sử dụng grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g'sẽ làm cho nó đối phó với tên tập tin tùy ý.
terdon

cảm ơn các bạn. thêm cập nhật và để lại mã cũ vì đó là một cảnh báo thú vị có thể hữu ích cho người không biết về hành vi này.
o_o_o--

6

Từ quan điểm của người dùng, một công cụ Unix đơn giản và tuyệt vời thực hiện công việc một cách hoàn hảo qsubst. Ví dụ,

% qsubst foo bar *.c *.h

sẽ thay thế foobằng bartrong tất cả các tệp C của tôi. Một tính năng hay là qsubstsẽ thực hiện thay thế truy vấn , nghĩa là, nó sẽ hiển thị cho tôi từng lần xuất hiện foovà hỏi tôi có muốn thay thế nó hay không. [Bạn có thể thay thế vô điều kiện (không hỏi) bằng -gotùy chọn và có các tùy chọn khác, ví dụ: -wnếu bạn chỉ muốn thay thế fookhi đó là toàn bộ từ.]

Cách lấy: qsubstđược phát minh bởi der Mouse (từ McGill) và được đăng lên comp.unix.source 11 (7) vào tháng 8 năm 1987. Phiên bản cập nhật tồn tại. Ví dụ, phiên bản NetBSD qsubst.c,v 1.8 2004/11/01biên dịch và chạy hoàn hảo trên máy mac của tôi.


2

Tôi cần một cái gì đó sẽ cung cấp tùy chọn chạy khô và sẽ hoạt động đệ quy với một quả địa cầu, và sau khi cố gắng thực hiện awksedtôi đã từ bỏ và thay vào đó đã làm nó bằng python.

Tập lệnh tìm kiếm đệ quy tất cả các tệp khớp với mẫu toàn cục (ví dụ --glob="*.html") cho biểu thức chính quy và thay thế bằng biểu thức thay thế:

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

Mỗi tùy chọn dài như --search-regexcó một tùy chọn ngắn tương ứng, nghĩa là -s. Chạy với -hđể xem tất cả các tùy chọn.

Ví dụ: điều này sẽ lật tất cả các ngày từ 2017-12-31đến 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 là phiên bản cập nhật của tập lệnh làm nổi bật các thuật ngữ tìm kiếm và thay thế bằng các màu khác nhau.


1
Tôi không hiểu tại sao bạn lại làm một cái gì đó phức tạp như vậy. Để đệ quy, hãy sử dụng tùy chọn bash's (hoặc tương đương với vỏ của bạn) globstarvà các khối **hoặc find. Đối với một chạy khô, chỉ cần sử dụng sed. Trừ khi bạn sử dụng -itùy chọn, nó sẽ không thực hiện bất kỳ thay đổi nào. Để sử dụng sao lưu sed -i.bak(hoặc perl -i .bak); đối với các tệp không khớp, sử dụng grep PATTERN file || echo file. Và tại sao trên thế giới bạn sẽ có con trăn mở rộng toàn cầu thay vì để vỏ làm điều đó? Tại sao script.py --glob=foo*thay vì chỉ script.py foo*?
terdon

1
My tại sao của tôi rất đơn giản: (1) trên tất cả, dễ gỡ lỗi; (2) chỉ sử dụng một công cụ được ghi chép tốt với một cộng đồng hỗ trợ (3) không biết sedawkkhông sẵn sàng đầu tư thêm thời gian để làm chủ chúng, (4) khả năng đọc, (5) giải pháp này cũng sẽ hoạt động trên các hệ thống không phải là posix (không phải tôi cần điều đó nhưng người khác có thể).
ccpizza

1

ripgrep (tên lệnh rg) là một grepcông cụ, nhưng cũng hỗ trợ tìm kiếm và thay thế.

$ 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 không hỗ trợ tùy chọn tại chỗ, vì vậy bạn sẽ phải tự làm

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


Xem tài liệu regex Rust cho các tính năng và cú pháp biểu thức chính quy. Việc -Pchuyển đổi sẽ cho phép hương vị PCRE2 . rghỗ trợ Unicode theo mặc định.

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


Giống như grep, -Ftùy chọn sẽ cho phép các chuỗi cố định được khớp, một tùy chọn tiện dụng mà tôi cảm thấy sedcũng nên thực hiện.

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


Một tùy chọn tiện dụng khác là -Ucho phép kết hợp nhiều dòng

$ # (?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 cũng có thể xử lý các tập tin kiểu 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


Một ưu điểm khác rglà nó có khả năng nhanh hơnsed

$ # 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.