Làm thế nào để grep và thay thế


251

Tôi cần tìm kiếm đệ quy một chuỗi được chỉ định trong tất cả các tệp và thư mục con trong một thư mục và thay thế chuỗi này bằng một chuỗi khác.

Tôi biết rằng lệnh để tìm nó có thể trông như thế này:

grep 'string_to_find' -r ./*

Nhưng làm thế nào tôi có thể thay thế mọi trường hợp string_to_findbằng một chuỗi khác?


Tôi không tin grep có thể làm điều này (tôi có thể sai). Cách dễ dàng hơn là sử dụng sed hoặc perl để thay thế
Memento Mori

2
Hãy thử sử dụngsed -i 's/.*substring.*/replace/'
Eddy_Em

2
@Eddy_Em Điều đó sẽ thay thế toàn bộ dòng thay thế. Bạn cần sử dụng nhóm để nắm bắt một phần của dòng trước và sau chuỗi con và sau đó đặt nó vào dòng thay thế. sed -i 's/\(.*\)substring\(.*\)/\1replace\2/'
JStrahl


Câu trả lời:


248

Một lựa chọn khác là sử dụng find và sau đó chuyển qua sed.

find /path/to/files -type f -exec sed -i 's/oldstring/new string/g' {} \;

34
Trên OS X 10.10 Terminal, cần có một chuỗi mở rộng thích hợp cho tham số -i. Ví dụ: find /path/to/files -type f -exec sed -i "" "s/oldstring/new string/g" {} \;Dù sao, việc cung cấp chuỗi trống vẫn tạo ra một tệp sao lưu không giống như được mô tả trong hướng dẫn ...
Eonil

10
Tại sao tôi nhận được "sed: RE error: chuỗi byte bất hợp pháp". Và vâng, tôi đã thêm -i ""cho OS X. Nó hoạt động khác.
taco

2
Tôi gặp vấn đề về chuỗi byte bất hợp pháp trên macOS 10.12 và câu hỏi / câu trả lời này đã giải quyết vấn đề của tôi: stackoverflow.com/questions/19242275/ .
abeboparebop

3
Điều này chạm vào mọi tập tin để thời gian tập tin được sửa đổi; và chuyển đổi các kết thúc dòng từ CRLFsang LFtrên Windows.
jww

183

Tôi đã có câu trả lời.

grep -rl matchstring somedir/ | xargs sed -i 's/string1/string2/g'

14
Điều này sẽ quét qua các tệp phù hợp hai lần ... một lần grepvà sau đó một lần nữa với sed. Sử dụng findphương pháp hiệu quả hơn nhưng phương pháp này bạn đề cập không hoạt động.
cmevoli

41
Trên OS X bạn sẽ cần phải thay đổi sed -i 's/str1/str2/g'để sed -i "" 's/str1/str2/g'cho điều này để làm việc.
jdf 7/07/2015

6
@cmevoli với phương pháp này, grepđi qua tất cả các tệp và sedchỉ quét các tệp khớp với grep. Với findphương thức trong câu trả lời khác, findtrước tiên , liệt kê tất cả các tệp và sau đó sedsẽ quét qua tất cả các tệp trong thư mục đó. Vì vậy, phương pháp này không nhất thiết phải là chậm, nó phụ thuộc vào có bao nhiêu trận đấu có và sự khác biệt về tốc độ tìm kiếm giữa sed, grepfind.
joelostblom

4
OTOH theo cách này cho phép bạn XEM TRƯỚC những gì grep tìm thấy TRƯỚC thực sự thay thế, giảm đáng kể nguy cơ thất bại, đặc biệt là đối với regex n00bs như tôi
Lennart Rolland

2
Điều này cũng hữu ích khi thay thế grep của bạn thông minh hơn sed. Ví dụ ripgrep tuân theo .gitignore trong khi sed thì không.
dùng31389

43

Bạn thậm chí có thể làm điều đó như thế này:

Thí dụ

grep -rl 'windows' ./ | xargs sed -i 's/windows/linux/g'

Điều này sẽ tìm kiếm chuỗi ' windows ' trong tất cả các tệp liên quan đến thư mục hiện tại và thay thế ' windows ' bằng ' linux ' cho mỗi lần xuất hiện của chuỗi trong mỗi tệp.


2
Điều grepnày chỉ hữu ích nếu có những tập tin không nên sửa đổi. Chạy sedtrên tất cả các tệp sẽ cập nhật ngày sửa đổi của tệp nhưng không thay đổi nội dung nếu không có kết quả khớp.
tripleee

@tripleee: Cẩn thận với ... nhưng [sed] rời khỏi các nội dung không thay đổi nếu không có trận đấu" Khi sử dụng. -i, tôi tin rằng sedthay đổi thời gian tập tin của tất cả các tập tin nó chạm vào, mặc dù nội dung không thay đổi. sedcũng chuyển đổi kết thúc dòng . Tôi không sử dụng sedtrên Windows trong repo Git vì tất cả CRLFđược đổi thành LF.
jww

Lệnh này cần một "" sau -i để biểu thị rằng sẽ không có tệp sao lưu nào được thực hiện sau khi thay thế tại chỗ diễn ra, ít nhất là trong macosx. Kiểm tra trang người đàn ông để biết chi tiết. Nếu bạn muốn sao lưu, đây là nơi bạn đặt phần mở rộng của tệp sẽ được tạo.
spinyBabbler

31

Điều này hoạt động tốt nhất với tôi trên OS X:

grep -r -l 'searchtext' . | sort | uniq | xargs perl -e "s/matchtext/replacetext/" -pi

Nguồn: http://www.praj.com.au/post/23691181208/grep-replace-text-opes-in-files


đây là hoàn hảo! cũng hoạt động với ag:ag "search" -l -r . | sort | uniq | xargs perl -e 's/search/replace' -pi

@sebastiankeller Lệnh Perl của bạn thiếu dấu gạch chéo cuối cùng, đây là lỗi cú pháp.
tripleee

3
Tại sao là sort -umột phần của điều này? Trong trường hợp nào bạn sẽ mong grep -rlmuốn tạo ra cùng một tên tệp hai lần?
tripleee

5

Các giải pháp khác trộn cú pháp regex. Để sử dụng các mẫu perl / PCRE cho cả tìm kiếm và thay thế và chỉ xử lý các tệp phù hợp, điều này hoạt động khá tốt:

grep -rlZPi 'match1' | xargs -0r perl -pi -e 's/match2/replace/gi;'

trong đó match1match2thường giống hệt nhau nhưng match1có thể được đơn giản hóa để loại bỏ các tính năng nâng cao hơn chỉ liên quan đến sự thay thế, ví dụ: các nhóm bắt giữ.

Dịch: grepđệ quy và liệt kê các tệp khớp với mẫu PCRE này, được phân tách bằng nul để bảo vệ bất kỳ ký tự đặc biệt nào trong tên tệp, sau đó chuyển các tên tệp xargsđó đang chờ danh sách được phân tách bằng nul, nhưng sẽ không làm gì nếu không nhận được tên, và có được perlcác dòng thay thế nơi tìm thấy kết quả phù hợp.

Thêm công Itắc grepđể bỏ qua các tệp nhị phân. Đối với kết hợp phân biệt chữ hoa chữ thường, thả công itắc từ grepicờ được gắn vào biểu thức thay thế, nhưng không phảiicông tắc trên perlchính nó.


Bản thân Perl hoàn toàn có khả năng đệ quy cấu trúc tệp. Trên thực tế, có một công cụ find2perlvận chuyển với Perl thực hiện loại công việc này mà không có bất kỳ xargsmánh khóe nào .
tripleee

@tripleee findkhông tìm kiếm nội dung tệp và vấn đề là chỉ xử lý các tệp phù hợp mà không viết chương trình Perl.
Walf

Đây là một giải pháp tốt cho Windows, vì nó tránh được vấn đề chuyển đổi các kết thúc dòng dựa trên sed. Cảm ơn!
JamHandy

4

Thường không phải với grep, mà là với sed -i 's/string_to_find/another_string/g'hoặc perl -i.bak -pe 's/string_to_find/another_string/g'.


3

Hãy thật cẩn thận khi sử dụng findsedtrong một repo git! Nếu bạn không loại trừ các tệp nhị phân, bạn có thể gặp phải lỗi này:

error: bad index file sha1 signature 
fatal: index file corrupt

Để giải quyết lỗi này, bạn cần hoàn nguyên sedbằng cách thay thế new_stringbằngold_string . Điều này sẽ hoàn nguyên các chuỗi thay thế của bạn, vì vậy bạn sẽ trở lại điểm bắt đầu của vấn đề.

Cách chính xác để tìm kiếm một chuỗi và thay thế nó là bỏ qua findvà sử dụng grepthay vào đó để bỏ qua các tệp nhị phân:

sed -ri -e "s/old_string/new_string/g" $(grep -Elr --binary-files=without-match "old_string" "/files_dir")

Tín dụng cho @hobs


1

Đây là những gì tôi sẽ làm:

find /path/to/dir -type f -iname "*filename*" -print0 | xargs -0 sed -i '/searchstring/s/old/new/g'

cái này sẽ tìm tất cả các tệp chứa filenametrong tên của tệp /path/to/dir, hơn là cho mọi tệp được tìm thấy, tìm kiếm dòng searchstringvà thay thế oldbằng new.

Mặc dù nếu bạn muốn bỏ qua việc tìm kiếm một tệp cụ thể với một filenamechuỗi trong tên của tệp, thì chỉ cần làm:

find /path/to/dir -type f -print0 | xargs -0 sed -i '/searchstring/s/old/new/g'

Điều này sẽ làm điều tương tự ở trên, nhưng với tất cả các tệp được tìm thấy bên dưới /path/to/dir.


0

Một lựa chọn khác là chỉ sử dụng perl với globalstar.

Kích hoạt shopt -s globstartrong .bashrc(hoặc bất cứ nơi nào) cho phép** mô hình khớp với tất cả các thư mục con và tệp theo cách đệ quy.

Do đó sử dụng perl -pXe 's/SEARCH/REPLACE/g' -i **sẽ thay thế đệ quy SEARCHbằngREPLACE .

Các -X lá cờ nói với perl để "vô hiệu hóa tất cả các cảnh báo" - có nghĩa là nó sẽ không phàn nàn về thư mục.

Glostar cũng cho phép bạn thực hiện những việc như sed -i 's/SEARCH/REPLACE/g' **/*.extnếu bạn muốn thay thế SEARCHbằng REPLACEtất cả các tệp con có phần mở rộng .ext.


"Một lựa chọn khác là chỉ sử dụng perl với globalstar ..." - Không phải trên các máy Posixy, như Solaris. Đó là lý do tại sao tôi đặc biệt tìm kiếm grepsed.
jww
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.