ค้นหาและแทนที่ใน Vim ในไฟล์โครงการทั้งหมด


117

ฉันกำลังมองหาวิธีที่ดีที่สุดในการค้นหาและแทนที่ (พร้อมการยืนยัน) ในไฟล์โครงการทั้งหมดใน Vim โดย "ไฟล์โครงการ" ฉันหมายถึงไฟล์ในไดเร็กทอรีปัจจุบันซึ่งบางไฟล์ไม่จำเป็นต้องเปิด

วิธีหนึ่งในการดำเนินการนี้คือเพียงแค่เปิดไฟล์ทั้งหมดในไดเร็กทอรีปัจจุบัน:

:args ./**

จากนั้นทำการค้นหาและแทนที่ไฟล์ที่เปิดอยู่ทั้งหมด:

:argdo %s/Search/Replace/gce

อย่างไรก็ตามเมื่อฉันทำสิ่งนี้การใช้งานหน่วยความจำของ Vim จะเพิ่มขึ้นจากสองสิบ MB เป็นมากกว่า 2 GB ซึ่งไม่ได้ผลสำหรับฉัน

ฉันยังมีEasyGrepปลั๊กอินติดตั้ง CtrlCแต่มันเกือบจะไม่เคยทำงาน-อย่างใดอย่างหนึ่งไม่พบเหตุการณ์ทั้งหมดหรือมันก็แฮงค์จนผมกด เพื่อให้ห่างไกลวิธีที่ต้องการของฉันที่จะทํางานนี้มันack-grepสำหรับคำค้นหาที่ใช้หน้าต่าง QuickFIX มันเปิดไฟล์ใด ๆ :bufdo %s/Search/Replace/gceที่มีคำว่าและไม่ได้เปิดมาก่อนและในที่สุด

ฉันกำลังมองหาปลั๊กอินที่ใช้งานได้ดีที่สามารถใช้สำหรับสิ่งนี้หรืออีกทางหนึ่งคือคำสั่ง / ลำดับของคำสั่งที่จะง่ายกว่าที่ฉันใช้อยู่ตอนนี้


1
@Cascabel เนื่องจากคุณเขียนความคิดเห็นนี้จึงมีไซต์ vi.stackexchange.com
Flimm

คำตอบ:


27

Greplaceทำงานได้ดีสำหรับฉัน

นอกจากนี้ยังมีเวอร์ชันที่พร้อมสำหรับเชื้อโรคใน github


8
ติดตั้งมันผมเห็นคำสั่งอื่น ๆ เช่น:Gsearchและ:Gbuffersearchอยู่ แต่เมื่อฉันพิมพ์ฉันได้รับ:Greplace Not an editor command: Greplace
Ramon Tayag

ตามเอกสารไวยากรณ์คือ: :Gsearch [<grep-option(s)>] [[<pattern>] [<filename(s)>]] ดังนั้นคุณสามารถทำการค้นหาแบบวนซ้ำได้โดยใช้ตัวอย่าง::Gsearch -r pattern dir/
vitorbal

สิ่งที่ฉันเกลียดเกี่ยวกับ Greplace คือถ้ารูปแบบอยู่ในชื่อไฟล์ Greplace จะล้มเหลวเพราะคุณจะแทนที่มันในชื่อไฟล์ด้วย
Daniel

2
ไม่มีอะไรผิดปกติที่นี่หลังจาก 6 ปี แต่ปลั๊กอินนี้ล้าสมัยอย่างมากเมื่อเทียบกับคำตอบใหม่ ๆ ( github.com/yegappan/greplace ) อัปเดตล่าสุดเมื่อ 3 ปีก่อน ฉันอาจตรวจสอบโซลูชันในตัวโดย Sid: stackoverflow.com/a/38004355/2224331 (ไม่ใช่ฉันในบัญชีอื่นเป็นเรื่องบังเอิญ: P)
SidOfc

99

ตัวเลือกใหญ่อื่น ๆ ที่นี่คือไม่ใช้กลุ่ม:

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

หรือหากคุณมีวิธีสร้างรายการไฟล์บางอย่างอาจเป็นดังนี้:

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

และอื่น ๆ !


ขอบคุณ! ฉันต้องการเรียนรู้สิ่งต่างๆในบรรทัดคำสั่งของโรงเรียนเก่านี้จริงๆ ผมคิดว่านี่คือคำตอบที่ดีที่สุด regexes perl regexes หรือ vim regexes หรือ regex อื่น ๆ
Eric Johnson

2
@EricJohnson: พวกมันคือ ... sedregexes ซึ่งโดยพื้นฐานแล้วเป็นนิพจน์ทั่วไปพื้นฐาน POSIX.2 แต่ไม่ตรง (นี่คือใน manpage) - พวกเขาปล่อยให้\nตรงกับบรรทัดใหม่และสิ่งอื่น ๆ ที่คล้ายกัน ดังนั้นมันจึงค่อนข้างเหมือนกับgrepนิพจน์ทั่วไป
Cascabel

มีเหตุผลที่คุณมี -i ในคำสั่ง sed หรือไม่? หน้าคนบอกว่ามีไว้สำหรับระบุส่วนขยาย แต่ดูเหมือนว่าคุณจะไม่ระบุส่วนขยายจริงๆ
davekaro

ตกลงดูเหมือนว่า 's / pattern / replacement /' เป็นส่วนขยายจริงๆฉันคิดว่าฉันเข้าใจแล้ว
davekaro

11
ใน Mac OS X คำสั่ง sed เดียวที่ใช้ได้กับฉันคือfind . -type f | LANG=C xargs sed -i '' 's/SEARCH/REPLACE/g'
ghodss

74

แก้ไข: ใช้cfdoคำสั่งแทนcdoเพื่อลดจำนวนคำสั่งที่จะรันเพื่อให้สำเร็จ (เนื่องจากcdoรันคำสั่งในแต่ละองค์ประกอบขณะcfdoรันคำสั่งในแต่ละไฟล์)

ด้วยcdoคำสั่งที่เพิ่งเพิ่มเข้ามาตอนนี้คุณสามารถทำได้ในสองคำสั่งง่ายๆโดยใช้grepเครื่องมืออะไรก็ได้ที่คุณติดตั้งไว้ ไม่จำเป็นต้องใช้ปลั๊กอินเพิ่มเติม!:

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

( cdoเรียกใช้คำสั่งที่กำหนดสำหรับแต่ละคำในรายการ Quickfix ซึ่งgrepคำสั่งของคุณจะเติมข้อมูล)

(ลบcท้ายคำสั่งที่ 2 หากคุณต้องการแทนที่คำค้นหาแต่ละคำโดยไม่ต้องยืนยันทุกครั้ง)


12
นอกจากนี้คุณสามารถเพิ่ม:cdo updateเพื่อบันทึกการเปลี่ยนแปลงในไฟล์ทั้งหมด
Zhe Li

1
แต่จะหยุดชั่วคราวเพื่อเตือนไม่ได้บันทึกในแต่ละครั้งก่อนที่จะเปลี่ยนเป็นบัฟเฟอร์ถัดไปเมื่อมีการแก้ไขบัฟเฟอร์ปัจจุบัน
ฟรี

1
@ เจ้ขอบคุณฉันได้เพิ่มคำตอบนั้นแล้ว @lfree ฉันชอบยืนยันการเปลี่ยนแปลงแต่ละครั้งเป็นการส่วนตัว แต่คุณสามารถลบcท้ายคำสั่งที่สอง (เพียงใช้g) เพื่อให้ค้นหาและแทนที่โดยไม่ต้องขอการยืนยัน
Sid

1
: cdo% s / ข้อความค้นหา / แทนที่คำ / g แทนที่เหตุการณ์แรกเท่านั้น แต่ใช้ไม่ได้กับเหตุการณ์ที่สองในกลุ่มของฉัน
sunxd

3
สำหรับการใช้updateงานฉันต้อง:cdo %s/<search term>/<replace term>/g | update
gabe

34

ฉันตัดสินใจใช้ ack และ Perl เพื่อแก้ปัญหานี้เพื่อใช้ประโยชน์จากนิพจน์ทั่วไป Perl แบบเต็มที่มีประสิทธิภาพมากกว่าชุดย่อย GNU

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

คำอธิบาย

แอ๊กชั่น

แอ๊กชั่นเป็นเครื่องมือบรรทัดคำสั่งที่น่ากลัวที่เป็นส่วนผสมของgrep, findและเต็มไปด้วยนิพจน์ปกติ Perl (ไม่ได้เป็นเพียงส่วนย่อย GNU) เขียนด้วย Perl บริสุทธิ์รวดเร็วมีการเน้นไวยากรณ์ทำงานบน Windows และเป็นมิตรกับโปรแกรมเมอร์มากกว่าเครื่องมือบรรทัดคำสั่งแบบเดิม ติดตั้งบน Ubuntu ด้วยsudo apt-get install ack-grep.

xargs

Xargsเป็นเครื่องมือบรรทัดคำสั่ง unix แบบเก่า อ่านรายการจากอินพุตมาตรฐานและรันคำสั่งที่ระบุตามด้วยรายการที่อ่านสำหรับอินพุตมาตรฐาน ดังนั้นโดยพื้นฐานแล้วรายการไฟล์ที่สร้างโดย ack จะถูกต่อท้ายperl -pi -E 's/pattern/replacemnt/g'คำสั่ง

perl -pi

Perl เป็นภาษาโปรแกรม อ็อพชัน -p ทำให้ Perl สร้างลูปรอบโปรแกรมของคุณซึ่งวนซ้ำอยู่เหนืออาร์กิวเมนต์ชื่อไฟล์ อ็อพชัน -i ทำให้ Perl แก้ไขไฟล์ในตำแหน่ง คุณสามารถแก้ไขสิ่งนี้เพื่อสร้างการสำรองข้อมูล อ็อพชัน -E ทำให้ Perl รันโค้ดหนึ่งบรรทัดที่ระบุเป็นโปรแกรม ในกรณีของเราโปรแกรมเป็นเพียงการทดแทน Perl regex สำหรับข้อมูลเพิ่มเติมเกี่ยว Perl perldoc perlrunเลือกบรรทัดคำสั่ง สำหรับข้อมูลเพิ่มเติมเกี่ยว Perl ดูhttp://www.perl.org/


ฉันชอบคำตอบนี้เพราะมันใช้ได้แม้กระทั่งกับการลงท้ายบรรทัดของ cygwin โปรดทราบว่ามันจะเพิ่ม \ r ต่อท้ายบรรทัดที่แก้ไข แต่เมื่อใช้ sed มันจะแทนที่ทุกบรรทัดใหม่ทำให้ความแตกต่างของคุณใช้ไม่ได้ Perl มีสติมากขึ้นเล็กน้อยและนี่เป็นหนึ่งซับที่ยอดเยี่ยมสำหรับการค้นหาและแทนที่ ขอบคุณ!
andrew

@andrew ฉันไม่แน่ใจว่าเกิดอะไรขึ้นในกรณีของคุณ จะช่วยให้คุณใช้ \ R แทน \ n ใน regexps ได้หรือไม่? ดูในส่วน R \ ในperldoc.perl.org/perlrebackslash.html#Misc ลองperldoc.perl.org/perlre.html#Regular-Expressionsด้วย Perl เป็นแพลทฟอร์มที่ยอดเยี่ยมมากและฉันแน่ใจว่ามีวิธีที่คุณจะไม่จบลงด้วยการจบบรรทัดที่ยุ่งเหยิง
Eric Johnson

regex ของฉันไม่มี newlines อยู่ในนั้นโปรแกรมเหล่านี้เวอร์ชัน cygwin จะเพิ่ม \ r โดยอัตโนมัติเมื่อสิ้นสุดแต่ละบรรทัดที่แก้ไข ฉันชอบเวอร์ชัน perl เป็นพิเศษเพราะมันปรับเปลี่ยนเฉพาะบรรทัดที่ตรงกันในขณะที่ sed จะแก้ไขทุกบรรทัดแม้ว่าจะไม่ตรงกับ regex ก็ตาม
andrew

จะเกิดอะไรขึ้นถ้าเส้นทางของไฟล์มีช่องว่าง?
shengy

23

อาจทำสิ่งนี้:

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

คำอธิบาย:

  • :noautocmd vim /Search/ **/*⇒ lookup ( vimเป็นคำย่อสำหรับvimgrep) รูปแบบในไฟล์ทั้งหมดในไดเร็กทอรีย่อยทั้งหมดของ cwd โดยไม่เรียกใช้ autocmds ( :noautocmd) เพื่อประโยชน์ในด้านความเร็ว
  • :set hidden ⇒อนุญาตให้มีการแก้ไขบัฟเฟอร์ไม่แสดงในหน้าต่าง (อาจอยู่ใน vimrc ของคุณ)
  • :cfirst ⇒ข้ามไปที่ผลการค้นหาแรก
  • qa ⇒เริ่มบันทึกมาโครลงในทะเบียนไฟล์
  • :%s//Replace/gce⇒แทนที่การเกิดขึ้นทั้งหมดของรูปแบบการค้นหาล่าสุด (ยัง/Search/อยู่ในเวลานั้น) ด้วยReplace:
    • หลายครั้งในบรรทัดเดียวกัน ( gแฟล็ก)
    • ด้วยการยืนยันผู้ใช้ ( cแฟล็ก)
    • โดยไม่มีข้อผิดพลาดหากไม่พบรูปแบบ ( eแฟล็ก)
  • :cnf⇒ข้ามไปที่ไฟล์ถัดไปในรายการที่สร้างโดยvimคำสั่ง
  • q ⇒หยุดบันทึกมาโคร
  • 1000@a ⇒เล่นมาโครที่เก็บไว้ในการลงทะเบียน 1,000 ครั้ง
  • :wa ⇒บันทึกบัฟเฟอร์ที่แก้ไขทั้งหมด

* แก้ไข * เป็นกลุ่ม 8 วิธี:

เริ่มต้นด้วย Vim 8 มีวิธีที่ดีกว่าในการทำเช่นนี้เนื่องจากการทำ:cfdoซ้ำในไฟล์ทั้งหมดในรายการแก้ไขด่วน:

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

คุณอาจต้องการอธิบายเพิ่มเติมอีกเล็กน้อยสำหรับพวกเราที่เป็นปุถุชนน้อย มันไม่ชัดเจนสำหรับฉันถ้าคุณต้องทำ "ลำดับ" นี้ทุกครั้ง ฉันทำได้เร็วขึ้นโดยใช้ Delphi 5 เก่าที่เป็นสนิมของฉันดังนั้นแน่นอนว่าฉันต้องขาดอะไร
Lieven Keersmaekers

1
@ ลีเวน: คุณพูดถูกนี่เป็นเรื่องที่คลุมเครือเล็กน้อยโดยไม่มีความคิดเห็น ฉันจะพัฒนาคำอธิบายบางอย่าง
Benoit

+1 แต่ถึงแม้ว่าvimฉันจะชนะในหลาย ๆ สิ่ง แต่ฉันคิดว่าฉันจะยึดติดกับ PowerGrep
Lieven Keersmaekers

ขอบคุณสำหรับคำตอบและคำอธิบายของคำสั่งทั้งหมดฉันซาบซึ้งจริงๆ ฉันยอมรับคำตอบอื่นเพราะฉันชอบใช้ปลั๊กอินสำหรับสิ่งนี้
psyho

ฉันได้ทำเครื่องหมายสิ่งนี้ไว้เนื่องจากทำให้เกิดความสับสนมากขึ้นเนื่องจากความแตกต่างระหว่างระดับของคนที่ถามคำถามและระดับที่จำเป็นในการทำความเข้าใจคำตอบนี้
Lloyd Moore

22

เติมข้อมูล:argsจากคำสั่งเชลล์

เป็นไปได้ (ในระบบปฏิบัติการบางระบบ1 )) ในการจัดหาไฟล์:argsผ่านคำสั่งเชลล์

ตัวอย่างเช่นหากคุณติดตั้งack 2ไว้

:args `ack -l pattern`

จะขอให้ ack ส่งคืนรายการไฟล์ที่มี 'pattern' และใส่ไว้ในรายการอาร์กิวเมนต์

หรือด้วย grep ol ธรรมดาฉันเดาว่ามันน่าจะเป็น:

:args `grep -lr pattern .`  


จากนั้นคุณสามารถใช้:argdoตามที่ OP อธิบาย:

:argdo %s/pattern/replacement/gce


เติมข้อมูล:argsจากรายการแก้ไขด่วน

ตรวจสอบคำตอบของ nelstromสำหรับคำถามที่เกี่ยวข้องซึ่งอธิบายถึงคำสั่งที่ผู้ใช้กำหนดอย่างง่ายซึ่งเติมข้อมูลรายการอาร์กิวเมนต์จากรายการแก้ไขด่วนปัจจุบัน ใช้งานได้ดีกับคำสั่งและปลั๊กอินจำนวนมากซึ่งผลลัพธ์จะลงเอยในรายการ Quickfix ( :vimgrep, :Ack3 , :Ggrep4 )

ลำดับในการค้นหาโครงการแบบกว้างสามารถทำได้ด้วย:

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

ที่:Qargsเป็นสายกับคำสั่งที่ผู้ใช้กำหนดที่ populates arglist จากรายการ QuickFIX

นอกจากนี้คุณจะพบลิงก์ในการสนทนาต่อไปนี้ไปยังปลั๊กอินง่ายๆที่ทำให้ขั้นตอนการทำงานนี้ลดลงเหลือ 2 หรือ 3 คำสั่ง

การเชื่อมโยง

  1. : h {arglist} - vimdoc.sourceforge.net/htmldoc/editing.html#{arglist}
  2. ack - betterthangrep.com/
  3. ack.vim - github.com/mileszs/ack.vim
  4. ผู้ลี้ภัย - github.com/tpope/vim-fugitive

1
argdo หยุดหลังจากไฟล์แรกที่มีปัญหาในการเขียน
frankster


5

หากคุณไม่สนใจที่จะแนะนำการพึ่งพาภายนอกฉันได้สร้างปลั๊กอินctrlsf.vim (ขึ้นอยู่กับackหรือag ) เพื่อทำงาน

สามารถจัดรูปแบบและแสดงผลการค้นหาจาก ack / ag และซิงโครไนซ์การเปลี่ยนแปลงของคุณในบัฟเฟอร์ผลลัพธ์ไปยังไฟล์จริงบนดิสก์

อาจจะอธิบายเพิ่มเติมตามการสาธิต

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

ขั้นตอนที่ 1 จะเติมข้อมูลในรายการแก้ไขด่วนด้วยรายการที่คุณต้องการ grepในกรณีนี้ก็แพร่กระจายด้วยคำค้นหาที่คุณต้องการเปลี่ยนผ่าน

cfdoรันคำสั่งต่อไปนี้ในแต่ละไฟล์ในรายการ Quickfix พิมพ์:help cfdoเพื่อดูรายละเอียด

s/<search term>/<replace term>/gแทนที่แต่ละคำ /gหมายถึงแทนที่ทุกเหตุการณ์ในไฟล์

| update บันทึกไฟล์หลังจากเปลี่ยนทุกครั้ง

ฉันรวบรวมสิ่งนี้เข้าด้วยกันตามคำตอบนี้และความคิดเห็น แต่รู้สึกว่ามันสมควรได้รับคำตอบของตัวเองเพราะทั้งหมดนี้รวมอยู่ในที่เดียว


สำหรับฉันใน NeoVim จำเป็นต้องทำการปรับเปลี่ยนเล็กน้อยในบรรทัดที่ 2 ด้านบน: 2. :cfdo %s/<search term>/<replace term>/g | updateหากไม่มี%รูปแบบที่เกิดขึ้นครั้งแรกจะถูกแทนที่เท่านั้น
Kareem Ergawy

ขอบคุณ Kareem ฉันอัปเดตคำตอบเนื่องจาก%sทำงานในกลุ่มปกติด้วย
Kyle Heironimus

0

โดยทั่วไปฉันต้องการแทนที่ในคำสั่งเดียวและที่สำคัญกว่านั้นคือภายในกลุ่มตัวเอง

จากคำตอบของ @Jefromiฉันได้สร้างแป้นพิมพ์ลัดซึ่งฉันได้ตั้งค่าไว้ในไฟล์. vimrc ของฉันเช่นนี้

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

ตอนนี้จากกลุ่มในจังหวะของผู้นำ + r ฉันได้รับคำสั่งที่โหลดเป็นกลุ่มซึ่งฉันแก้ไขดังนี้

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

ดังนั้นฉันจะแทนที่ด้วยคำสั่งเดียวและที่สำคัญกว่านั้นคือภายในกลุ่มตัวเอง


และฉันใช้ ag แทน grep จริงๆ
deepak
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.