Tìm kiếm và thay thế trong Vim trên tất cả các tệp dự án


117

Tôi đang tìm cách tốt nhất để thực hiện tìm kiếm và thay thế (có xác nhận) trên tất cả các tệp dự án trong Vim. Bởi "tệp dự án" ý tôi là các tệp trong thư mục hiện tại, một số tệp trong số đó không cần phải mở.

Một cách để làm điều này có thể là chỉ cần mở tất cả các tệp trong thư mục hiện tại:

:args ./**

và sau đó thực hiện tìm kiếm và thay thế trên tất cả các tệp đang mở:

:argdo %s/Search/Replace/gce

Tuy nhiên, khi tôi làm điều này, mức sử dụng bộ nhớ của Vim tăng từ vài chục MB lên hơn 2 GB, điều này không phù hợp với tôi.

Tôi cũng đã cài đặt plugin EasyGrep , nhưng nó hầu như không bao giờ hoạt động — hoặc nó không tìm thấy tất cả các lần xuất hiện hoặc nó chỉ bị treo cho đến khi tôi nhấn CtrlC. Cho đến nay tôi thích cách để hoàn thành nhiệm vụ này nó để ack-grep cho thuật ngữ tìm kiếm, sử dụng cửa sổ QuickFix nó mở bất kỳ tập tin nào có chứa cụm và không được mở trước đó, và cuối cùng :bufdo %s/Search/Replace/gce.

Tôi đang tìm kiếm một plugin tốt, hoạt động có thể được sử dụng cho việc này, hoặc cách khác là một lệnh / chuỗi lệnh sẽ dễ dàng hơn so với plugin tôi đang sử dụng hiện tại.


1
@Cascabel Kể từ khi bạn viết nhận xét này, có một trang web vi.stackexchange.com.
Flimm

Câu trả lời:


27

Greplace hoạt động tốt cho tôi.

Cũng có một phiên bản sẵn sàng cho mầm bệnh trên github.


8
Cài đặt nó, tôi thấy các lệnh khác giống như :Gsearch:Gbuffersearchtồn tại, nhưng khi tôi nhập, :Greplacetôi nhận được Not an editor command: Greplace.
Ramon Tayag

theo tài liệu, cú pháp là: :Gsearch [<grep-option(s)>] [[<pattern>] [<filename(s)>]] vì vậy bạn có thể thực hiện tìm kiếm đệ quy bằng cách sử dụng, ví dụ::Gsearch -r pattern dir/
vitorbal

Điều tôi ghét ở Greplace là nếu mẫu nằm trong tên tệp, Greplace sẽ không thành công, vì bạn cũng sẽ thay thế nó trong tên tệp.
Daniel

2
Không có gì bất thường ở đây sau 6 năm, nhưng plugin này đã lỗi thời nghiêm trọng so với một số câu trả lời mới ( github.com/yegappan/greplace ) bản cập nhật gần đây nhất, hơn 3 năm trước. Tôi có thể kiểm tra giải pháp tích hợp của Sid: stackoverflow.com/a/38004355/2224331 (Không phải tôi ở tài khoản khác, chỉ là sự trùng hợp: P)
SidOfc

99

Một lựa chọn lớn khác ở đây chỉ đơn giản là không sử dụng vim:

sed -i 's/pattern/replacement/' <files>

hoặc nếu bạn có một số cách để tạo danh sách các tệp, có thể giống như sau:

find . -name *.cpp | xargs sed -i 's/pattern/replacement/'
grep -rl 'pattern1' | xargs sed -i 's/pattern2/replacement/'

và như thế!


Cảm ơn! Tôi thực sự cần phải học những thứ về dòng lệnh cũ này. Tôi nghĩ đây là câu trả lời tốt nhất. Đây có phải là những regex perl regexes hay vim regexes hay một số loại regex khác không?
Eric Johnson

2
@EricJohnson: Chúng là ... sedregexes, về cơ bản là các biểu thức chính quy cơ bản POSIX.2, nhưng không chính xác (đây là trong manpage) - chúng cho phép \nkhớp với dòng mới và một số thứ tương tự khác. Do đó, chúng khá giống với grepbiểu thức chính quy.
Cascabel

Có lý do gì bạn có -i trong lệnh sed? Trang người đàn ông nói rằng đó là để chỉ định một tiện ích mở rộng, nhưng bạn dường như không thực sự chỉ định một tiện ích mở rộng.
davekaro

Ok, có vẻ như 's / pattern / Replace /' là phần mở rộng, tôi nghĩ bây giờ tôi đã hiểu.
davekaro

11
Trên Mac OS X sed lệnh duy nhất mà làm việc đối với tôi là:find . -type f | LANG=C xargs sed -i '' 's/SEARCH/REPLACE/g'
ghodss

74

CHỈNH SỬA: Sử dụng cfdolệnh thay vì cdođể giảm đáng kể số lượng lệnh sẽ được chạy để thực hiện điều này (vì cdochạy lệnh trên từng phần tử trong khi cfdochạy lệnh trên mỗi tệp)

Nhờ cdolệnh được thêm gần đây , bây giờ bạn có thể thực hiện việc này bằng hai lệnh đơn giản bằng bất kỳ grepcông cụ nào bạn đã cài đặt. Không cần bổ sung plugin !:

1. :grep <search term>
2. :cdo %s/<search term>/<replace term>/gc
3. (If you want to save the changes in all files) :cdo update

( cdothực hiện lệnh đã cho cho từng thuật ngữ trong danh sách tiền tố nhanh, mà greplệnh của bạn sẽ điền vào.)

(Bỏ phần ccuối của lệnh thứ 2 nếu bạn muốn thay thế từng cụm từ tìm kiếm mà không cần xác nhận mỗi lần)


12
Ngoài ra, bạn có thể thêm :cdo updateđể lưu các thay đổi trong tất cả các tệp.
Zhe Li

1
Nhưng nó sẽ tạm dừng để cảnh báo không được lưu mỗi lần trước khi chuyển sang bộ đệm tiếp theo khi bộ đệm hiện tại được sửa đổi.
miễn phí

1
@Zhe cảm ơn, tôi đã thêm điều đó vào câu trả lời; @lfree Cá nhân tôi thích xác nhận từng thay đổi, nhưng bạn có thể loại bỏ các cvào cuối của lệnh thứ hai (chỉ cần sử dụng g) để có nó tìm kiếm và thay thế mà không yêu cầu xác nhận
Sid

1
: CDO% s / thuật ngữ tìm kiếm / thay thế hạn / g chỉ thay thế sự xuất hiện đầu tiên, nhưng không làm việc cho sự xuất hiện thứ hai trong vim tôi
sunxd

3
để sử dụng update, tôi phải:cdo %s/<search term>/<replace term>/g | update
gabe

34

Tôi đã quyết định sử dụng ack và Perl để giải quyết vấn đề này nhằm tận dụng các biểu thức chính quy Perl đầy đủ mạnh hơn thay vì tập con GNU.

ack -l 'pattern' | xargs perl -pi -E 's/pattern/replacement/g'

Giải trình

ack

ack là một công cụ dòng lệnh tuyệt vời mà là một kết hợp của grep, findvà đầy đủ Perl biểu thức thông thường (không chỉ là tập hợp con GNU). Nó được viết bằng Perl thuần túy, tốc độ nhanh, có tính năng đánh dấu cú pháp, hoạt động trên Windows và thân thiện hơn với các lập trình viên so với các công cụ dòng lệnh truyền thống. Cài đặt nó trên Ubuntu với sudo apt-get install ack-grep.

xargs

Xargs là một công cụ dòng lệnh unix cũ. Nó đọc các mục từ đầu vào tiêu chuẩn và thực hiện lệnh được chỉ định, theo sau là các mục được đọc cho đầu vào tiêu chuẩn. Vì vậy, về cơ bản danh sách các tệp được tạo bởi ack đang được thêm vào cuối perl -pi -E 's/pattern/replacemnt/g'lệnh.

perl -pi

Perl là một ngôn ngữ lập trình. Tùy chọn -p khiến Perl tạo một vòng lặp xung quanh chương trình của bạn để lặp lại các đối số tên tệp. Tùy chọn -i khiến Perl chỉnh sửa tệp tại chỗ. Bạn có thể sửa đổi điều này để tạo bản sao lưu. Tùy chọn -E khiến Perl thực thi một dòng mã được chỉ định làm chương trình. Trong trường hợp của chúng tôi, chương trình chỉ là một thay thế Perl regex. Để biết thêm thông tin về các tùy chọn dòng lệnh Perl perldoc perlrun. Để biết thêm thông tin về Perl, hãy xem http://www.perl.org/ .


Tôi thích câu trả lời này vì nó hoạt động ngay cả với phần cuối dòng của cygwin. Lưu ý rằng nó có thêm \ r vào cuối các dòng đã sửa đổi, nhưng khi sử dụng sed, nó sẽ thay thế mọi dòng mới, làm cho các dòng khác của bạn không thể sử dụng được. Perl lành mạnh hơn một chút, và đây là một phần mềm thực sự tuyệt vời để tìm kiếm và thay thế. Cảm ơn!
andrew

@andrew, tôi không chắc chính xác có chuyện gì xảy ra trong trường hợp của bạn. Nó có giúp bạn sử dụng \ R thay vì \ n trong regexps của mình không? Xem phần \ R trong perldoc.perl.org/perlrebackslash.html#Misc . Ngoài ra, hãy thử perldoc.perl.org/perlre.html#Regular-Expressions . Perl là nền tảng cực kỳ đa dạng và tôi chắc chắn rằng có một cách để bạn không phải kết thúc với phần cuối dòng bị lệch.
Eric Johnson

Regex của tôi không có bất kỳ dòng mới nào trong đó, phiên bản cygwin của các chương trình này sẽ tự động thêm \ r vào cuối mỗi dòng được sửa đổi. Tôi đặc biệt thích phiên bản perl vì nó chỉ sửa đổi các dòng mà nó phù hợp trong khi sed sẽ sửa đổi tất cả các dòng, ngay cả khi chúng không khớp với regex.
andrew

Điều gì sẽ xảy ra nếu đường dẫn của tệp chứa khoảng trắng?
shengy

23

có thể làm điều này:

:noautocmd vim /Search/ **/*
:set hidden
:cfirst
qa
:%s//Replace/gce
:cnf
q
1000@a
:wa

Giải trình:

  • :noautocmd vim /Search/ **/*⇒ mẫu tra cứu ( vimlà viết tắt của vimgrep) trong tất cả các tệp trong tất cả các thư mục con của cwd mà không kích hoạt autocmds ( :noautocmd), vì lợi ích của tốc độ.
  • :set hidden ⇒ cho phép không hiển thị bộ đệm đã sửa đổi trong cửa sổ (có thể trong vimrc của bạn)
  • :cfirst ⇒ chuyển đến kết quả tìm kiếm đầu tiên
  • qa ⇒ bắt đầu ghi một macro vào đăng ký một
  • :%s//Replace/gce⇒ thay thế tất cả các lần xuất hiện của mẫu tìm kiếm cuối cùng (vẫn còn /Search/tại thời điểm đó) bằng Replace:
    • nhiều lần trên cùng một dòng ( gcờ)
    • với xác nhận của người dùng ( ccờ)
    • không có lỗi nếu không tìm thấy mẫu ( ecờ)
  • :cnf⇒ chuyển đến tệp tiếp theo trong danh sách được tạo bởi vimlệnh
  • q ⇒ dừng ghi macro
  • 1000@a ⇒ chơi macro được lưu trữ trong thanh ghi 1000 lần
  • :wa ⇒ lưu tất cả các bộ đệm đã sửa đổi

* CHỈNH SỬA * Vim 8 cách:

Bắt đầu với Vim 8, có một cách tốt hơn để làm điều đó, khi :cfdolặp lại trên tất cả các tệp trong danh sách Quickfix:

:noautocmd vim /Search/ **/*
:set hidden
:cfdo %s//Replace/gce
:wa

bạn có thể muốn giải thích điều đó xa hơn một chút cho chúng ta những người thấp bé hơn. Tôi không rõ liệu bạn có cần thực hiện "trình tự" này mọi lúc không. Tôi làm điều đó nhanh hơn bằng cách sử dụng Delphi 5 cũ rỉ sét của mình nên chắc chắn tôi phải thiếu thứ gì đó.
Lieven Keersmaekers

1
@Lieven: Bạn nói đúng, cái này hơi tối nghĩa không có ý kiến. Tôi sẽ phát triển một số giải thích.
Benoit

+1 nhưng mặc dù vimđã chiến thắng tôi về nhiều thứ, tôi nghĩ rằng tôi sẽ gắn bó với PowerGrep vì điều này.
Lieven Keersmaekers

Cảm ơn câu trả lời của bạn và giải thích về tất cả các lệnh, tôi thực sự đánh giá cao nó. Tôi chấp nhận câu trả lời khác vì tôi thích sử dụng một plugin cho việc này.
psyho

Tôi đã đánh dấu điều này xuống vì nó gây ra nhiều nhầm lẫn hơn, do sự chênh lệch giữa trình độ của người đặt câu hỏi và mức độ cần thiết để hiểu câu trả lời này.
Lloyd Moore

22

Điền :argstừ một lệnh shell

Có thể (trên một số hệ điều hành 1 )) để cung cấp các tệp :argsthông qua một lệnh shell.

Ví dụ: nếu bạn đã cài đặt ack 2 ,

:args `ack -l pattern`

sẽ yêu cầu ack trả về danh sách các tệp có chứa 'mẫu' và đưa chúng vào danh sách đối số.

Hoặc với ol 'grep đơn giản, tôi đoán nó sẽ là:

:args `grep -lr pattern .`  


Sau đó, bạn có thể chỉ cần sử dụng :argdo như mô tả của OP:

:argdo %s/pattern/replacement/gce


Điền :argstừ danh sách Quickfix

Ngoài ra, hãy xem câu trả lời của nelstrom cho một câu hỏi liên quan mô tả một lệnh đơn giản do người dùng xác định sẽ điền vào danh sách đối số từ danh sách Quickfix hiện tại. Điều này hoạt động tuyệt vời với nhiều lệnh và plugin có đầu ra kết thúc trong danh sách Quickfix ( :vimgrep, :Ack3 , :Ggrep4 ).

Sau đó, trình tự để thực hiện tìm kiếm trên toàn dự án có thể được thực hiện với:

:vimgrep /pattern/ **/*
:Qargs 
:argdo %s/findme/replacement/gc

đâu :Qargslà lệnh gọi đến lệnh do người dùng xác định sẽ điền vào danh sách đối số từ danh sách tiền tố nhanh.

Bạn cũng sẽ tìm thấy các liên kết trong cuộc thảo luận tiếp theo tới các plugin đơn giản giúp quy trình làm việc này giảm xuống còn 2 hoặc 3 lệnh.

Liên kết

  1. : h {arglist} - vimdoc.sourceforge.net/htmldoc/editing.html#{arglist}
  2. ack - betterthangrep.com/
  3. ack.vim - github.com/mileszs/ack.vim
  4. fugitive - github.com/tpope/vim-fugitive

1
argdo dừng sau tệp đầu tiên có vấn đề ghi
Frankster


5

Nếu bạn không ngại giới thiệu phụ thuộc bên ngoài, tôi đã tạo một plugin ctrlsf.vim (phụ thuộc vào ack hoặc ag ) để thực hiện công việc.

Nó có thể định dạng và hiển thị kết quả tìm kiếm từ ack / ag, đồng thời đồng bộ hóa các thay đổi của bạn trong bộ đệm kết quả với các tệp thực trên đĩa.

Có thể sau demo giải thích thêm

ctrlsf_edit_demo


3
1. :grep <search term> (or whatever you use to populate the quickfix window)
2. :cfdo %s/<search term>/<replace term>/g | update

Bước 1 điền vào danh sách Quickfix với các mục bạn muốn. Trong trường hợp này, nó được phổ biến với các cụm từ tìm kiếm mà bạn muốn thay đổi grep.

cfdochạy lệnh sau trên mỗi tệp trong danh sách sửa nhanh. Nhập :help cfdođể biết chi tiết.

s/<search term>/<replace term>/gthay thế từng thuật ngữ. /gcó nghĩa là thay thế mọi lần xuất hiện trong tệp.

| update lưu tệp sau mỗi lần thay thế.

Tôi kết hợp điều này với nhau dựa trên câu trả lời này và nhận xét của nó, nhưng cảm thấy nó xứng đáng có câu trả lời riêng vì tất cả đều ở một nơi.


Đối với tôi trên NeoVim, cần phải thực hiện một sửa đổi nhỏ trong dòng 2 ở trên: 2. :cfdo %s/<search term>/<replace term>/g | updateKhông có %, chỉ lần xuất hiện đầu tiên của mẫu được thay thế.
Kareem Ergawy

Cảm ơn Kareem. Tôi đã cập nhật câu trả lời vì %shoạt động trên vim thông thường.
Kyle Heironimus

0

Về cơ bản, tôi muốn thay thế trong một lệnh duy nhất và quan trọng hơn là trong chính vim

Dựa trên câu trả lời của @Jefromi, tôi đã tạo một phím tắt mà tôi đã đặt trong tệp .vimrc của mình như thế này

nmap <leader>r :!grep -r -l  * \| xargs sed -i -e 's///g'

bây giờ từ vim, trên một nét vẽ của leader + r, tôi nhận được lệnh được tải trong vim, mà tôi chỉnh sửa như bên dưới,

:!grep -r -l <find> <file pattern> | xargs sed -i -e 's/<find>/<replace>/g'

Do đó, tôi thực hiện việc thay thế bằng một lệnh duy nhất và quan trọng hơn là trong chính vim


và tôi thực sự sử dụng ag thay vì grep
deepak
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.