Sử dụng sed và grep / egrep để tìm kiếm và thay thế


97

Tôi đang sử dụng egrep -Rtheo sau là một biểu thức chính quy có chứa khoảng 10 liên hiệp, chẳng hạn như: .jpg | .png | .gifv.v ... Điều này hoạt động tốt, bây giờ tôi muốn thay thế tất cả các chuỗi được tìm thấy bằng.bmp

Tôi đã nghĩ về một cái gì đó như

egrep -lR "\.jpg|\.png|\.gif" . | sed "s/some_expression/.jpg/" file_it_came_form

vì vậy vấn đề ở đây là làm cách nào để thực hiện một biểu thức chính quy liên hiệp tương tự sedvà làm cách nào để yêu cầu nó lưu các thay đổi đối với tệp mà nó lấy đầu vào.


2
Tôi đã tìm thấy câu hỏi này khi đang tìm cách tìm kiếm và thay thế trên nhiều tệp trong hệ thống phân cấp thư mục. Đối với những người khác trong hoàn cảnh của tôi, hãy thử rpl .
titandecoy

cảm ơn bạn rpl hoạt động và thực sự dễ nhớ .. chỉ cần rpl old_string new_string target_files.
cesarpachon

Câu trả lời:


183

Sử dụng lệnh này:

egrep -lRZ "\.jpg|\.png|\.gif" . \
    | xargs -0 -l sed -i -e 's/\.jpg\|\.gif\|\.png/.bmp/g'
  • egrep: tìm các dòng phù hợp bằng cách sử dụng biểu thức chính quy mở rộng

    • -l: chỉ liệt kê các tên tệp phù hợp

    • -R: tìm kiếm đệ quy qua tất cả các thư mục nhất định

    • -Z: sử dụng \0làm dấu phân tách bản ghi

    • "\.jpg|\.png|\.gif": Phù hợp với một trong những chuỗi ".jpg", ".gif"hoặc".png"

    • .: bắt đầu tìm kiếm trong thư mục hiện tại

  • xargs: thực hiện một lệnh với đối số là stdin

    • -0: dùng \0làm dấu phân tách bản ghi. Đây là điều quan trọng để phù hợp với -Zcác egrepvà để tránh bị lừa bởi không gian và dòng mới trong tên tập tin đầu vào.

    • -l: sử dụng một dòng cho mỗi lệnh làm tham số

  • sed: the s tream ed itor

    • -i: thay thế tệp đầu vào bằng tệp đầu ra mà không cần sao lưu

    • -e: sử dụng đối số sau làm biểu thức

    • 's/\.jpg\|\.gif\|\.png/.bmp/g': Thay thế tất cả các lần xuất hiện của chuỗi ".jpg", ".gif"hoặc ".png"với".bmp"


tất cả đều hoạt động ngoại trừ | trong phần sed. Tôi không hiểu tại sao mặc dù nó có ý nghĩa ... phần -l của xargs đã cho tôi lỗi nên tôi đã lấy nó ra, điều đó có thể liên quan?
Ori

Tôi thấy rằng lệnh này thêm một dòng mới vào cuối tất cả các tệp mà nó xử lý.
titandecoy

@titanumdecoy: Tôi không thể tái tạo hành vi này. bạn đang sử dụng phiên bản sed nào và bạn đang sử dụng hệ điều hành nào?
David Schmitt,

1
@DavidSchmitt: Bạn có thể muốn sử dụng sed -rcho các biểu thức chính quy mở rộng. Tại thời điểm đó, mẫu sẽ khớp với những gì được sử dụng trong egrep và bạn có thể muốn đặt nó vào một biến để sử dụng lại.
bukzor

lệnh này đã giúp tôi tiết kiệm hàng giờ làm việc sao chép tệp tiêu đề ra khỏi ứng dụng của tôi cho thư viện mà tôi đã tạo. Điều này thật tuyệt vời :) Đây là lệnh tôi đã sử dụng egrep -lRZ "\ .h $". | xargs -0 tar -cvf headers.tar | (tiêu đề headers.tar cp; header cd; tar xf headers.tar;)
The Lazy Coder

11

Thành thật mà nói, tôi yêu thích sed cho các nhiệm vụ thích hợp, đây chắc chắn là một nhiệm vụ cho perl - nó thực sự mạnh mẽ hơn đối với loại một lớp lót này, đặc biệt là để "viết nó trở lại nơi nó xuất phát" ( -icông tắc của perl làm điều đó cho bạn, và tùy chọn cũng cho phép bạn giữ phiên bản cũ xung quanh, ví dụ như với .bak được thêm vào, chỉ cần sử dụng -i.bakthay thế).

perl -i.bak -pe 's/\.jpg|\.png|\.gif/.jpg/

chứ không phải là công việc phức tạp trong sed (nếu thậm chí có thể ở đó) hoặc awk ...


6
sed sử dụng -i, giống như perl.
Stobor

@Stobor - Tôi thề rằng tôi đã gặp vấn đề trong đó hoạt động perl khi tôi cấp chuỗi thay thế regex thực hiện chính xác những gì tôi muốn, không giống như sed, ngay cả khi tôi đã đưa tùy chọn regex cho sed.. Tôi nghĩ rằng tôi đã quên một số cờ cho sed hoặc nó có những hạn chế.
meder omuraliev

10

Một cách khác để làm điều này

find . -name *.xml -exec sed -i "s/4.6.0-SNAPSHOT/5.0.0-SNAPSHOT/" {} \;

Một số trợ giúp về lệnh trên

Tìm kiếm sẽ thực hiện tìm kiếm cho bạn trên thư mục hiện tại được chỉ định bởi .

-name tên của tệp trong trường hợp của tôi là pom.xml của nó có thể cung cấp các thẻ đại diện.

-exec hành hình

sed trình chỉnh sửa luồng

-i bỏ qua trường hợp

s là để thay thế

/4.6.0.../ Chuỗi được tìm kiếm

/5.0.0.../ Chuỗi được thay thế


có lẽ không phải là mạnh mẽ - nhưng điều này là dễ dàng hơn nhiều cho tôi để hiểu hơn câu trả lời được chấp nhận
icc97

5

Tôi không thể nhận được bất kỳ lệnh nào trên trang này để làm việc cho tôi: giải pháp sed đã thêm một dòng mới vào cuối tất cả các tệp mà nó xử lý và giải pháp perl không thể chấp nhận đủ đối số từ tìm thấy. Tôi tìm thấy giải pháp này hoạt động hoàn hảo:

find . -type f -name '*.[hm]' -print0 
    | xargs -0 perl -pi -e 's/search_regex/replacement_string/g'

Thao tác này sẽ lặp lại cây thư mục hiện tại và thay thế search_regexbằng replacement_stringbất kỳ tệp nào có đuôi là .hhoặc .m.

Tôi cũng đã sử dụng rpl cho mục đích này trong quá khứ.


0

thử một cái gì đó bằng cách sử dụng vòng lặp for

 for i in `egrep -lR "YOURSEARCH" .` ; do echo  $i; sed 's/f/k/' <$i >/tmp/`basename $i`; mv /tmp/`basename $i` $i; done

không đẹp, nhưng nên làm.


3
xargs chắc chắn là thích hợp hơn ở đây.
Nathan Fellman,

1
và sử dụng các |while read imô hình sẽ cho phép truyền và hạn chế chiều dài tránh khi kết quả egrep của trở nên quá dài
David Schmitt

0

Trường hợp sử dụng của tôi là tôi muốn thay thế foo:/Drive_Letterbằng foo:/bar/baz/xyz Trong trường hợp của tôi, tôi có thể làm điều đó với mã sau. Tôi đã ở cùng một vị trí thư mục, nơi có nhiều tệp.

find . -name "*.library" -print0 | xargs -0 sed -i '' -e 's/foo:\/Drive_Letter:/foo:\/bar\/baz\/xyz/g'

hy vọng rằng đã giúp.

CẬP NHẬT | foo: / Drive_letter: | foo: / ba / baz / xyz | g


Bạn có thể sử dụng delimiters khác cho lệnh sed, và làm như vậy làm cho đường dẫn Tìm nhiều đẹp hơn:sed 's|foo:/Drive_letter:|foo:/ba/baz/xyz|g'
Kevin
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.