รวมหลายบรรทัด (สองบล็อก) เป็นกลุ่ม


323

ฉันต้องการรวมสองบรรทัดของบล็อกใน Vim นั่นคือนำเส้นn..mและต่อท้ายบรรทัดa..bนั้น หากคุณต้องการคำอธิบาย pseudocode:[a[i] + b[i] for i in min(len(a), len(b))]

ตัวอย่าง:

abc
def
...

123
45
...

ควรกลายเป็น

abc123
def45

มีวิธีที่ดีในการทำเช่นนี้โดยไม่ทำการคัดลอกและวางด้วยตนเองหรือไม่?


ดังนั้น ... คุณต้องการเข้าร่วมสายการสลับ? นั่นคือคุณต้องการเข้าร่วมสายxกับเข้าร่วมx+2?
ลาร์สก์

1
ไม่ฉันมีสองช่วงตึกแยกกัน Pseudocode-ish:[a[i] + b[i] for i in min(len(a), len(b))]
ThiefMaster

2
ดูคำถามที่คล้ายกัน (และคำตอบ!) ที่นี่
NWS

คำตอบ:


880

คุณสามารถทำสิ่งนี้ได้อย่างแน่นอนด้วยการคัดลอก / วาง (ใช้การเลือกโหมดบล็อก) แต่ฉันเดาว่านั่นไม่ใช่สิ่งที่คุณต้องการ

หากคุณต้องการทำสิ่งนี้ด้วยคำสั่งEx

:5,8del | let l=split(@") | 1,4s/$/\=remove(l,0)/

จะเปลี่ยน

work it 
make it 
do it 
makes us 
harder
better
faster
stronger
~

เข้าไป

work it harder
make it better
do it faster
makes us stronger
~

UPDATE:คำตอบของ upvotes นี้สมควรได้รับคำอธิบายที่ละเอียดยิ่งขึ้น

ในกลุ่มคุณสามารถใช้ไพพ์ไพพ์ ( |) เพื่อโยงคำสั่ง Ex หลายคำสั่งได้ดังนั้นคำสั่งข้างต้นจึงเทียบเท่า

:5,8del
:let l=split(@")
:1,4s/$/\=remove(l,0)/

คำสั่ง Ex จำนวนมากยอมรับช่วงของบรรทัดเป็นอาร์กิวเมนต์ส่วนนำหน้า - ในกรณีข้างต้น5,8ก่อนdelและ1,4ก่อนหน้านั้นs///ระบุว่าบรรทัดใดที่คำสั่งทำงาน

delลบบรรทัดที่กำหนด มันสามารถรับอาร์กิวเมนต์ register แต่เมื่อไม่มีการกำหนดมันจะทิ้งบรรทัดไปยัง register ที่ไม่มีชื่อ@"เช่นเดียวกับการลบในโหมดปกติ let l=split(@")จากนั้นแยกบรรทัดที่ถูกลบออกเป็นรายการโดยใช้ตัวคั่นเริ่มต้น: ช่องว่าง ในการทำงานอย่างถูกต้องกับอินพุตที่มีช่องว่างในบรรทัดที่ถูกลบเช่น:

more than 
hour 
our 
never 
ever
after
work is
over
~

เราจะต้องระบุตัวคั่นที่แตกต่างกันเพื่อป้องกันไม่ให้ "งานคือ" let l=split(@","\n")จากการถูกแยกออกเป็นสององค์ประกอบของรายการ:

สุดท้ายในการทดแทนs/$/\=remove(l,0)/เราเปลี่ยนจุดสิ้นสุดของแต่ละบรรทัด (คน$) remove(l,0)โดยมีมูลค่าของการแสดงออก remove(l,0)เปลี่ยนแปลงรายการlลบและส่งคืนองค์ประกอบแรก สิ่งนี้ช่วยให้เราสามารถแทนที่บรรทัดที่ถูกลบตามลำดับที่เราอ่านได้ remove(l,-1)เราแทนสามารถใช้ทดแทนเส้นที่ถูกลบในลำดับที่กลับโดยใช้


1
อืม ... ฉันต้องกด Enter ครั้งเดียวเท่านั้น และมันจะไม่แทรกช่องว่างระหว่างสองส่วน หากมีช่องว่างต่อท้ายบนบรรทัด (เช่นในตัวอย่าง "ทำงานด้วย") นั่นจะยังคงอยู่ คุณสามารถกำจัดของพื้นที่ต่อท้ายใด ๆ ที่ใช้แทนs/\s*$/ s/$/
rampion

1
ขอบคุณสำหรับคำอธิบาย :sil5,8del | let l=split(@") | sil1,4s/$/\=remove(l,0)/ | call histdel("/", -1) | nohlsดูเหมือนจะดียิ่งขึ้นเนื่องจากล้างประวัติการค้นหาหลังจากใช้งาน และมันไม่แสดงข้อความ "x มาก / น้อยบรรทัด" ที่ต้องการให้ฉันกด Enter
ThiefMaster

11
หากคุณต้องการการอ้างอิงที่เป็นกลุ่มเต็มรูปแบบสำหรับคำตอบที่: :help range, :help :d, :help :let, :help split(), :help :s, ,:help :s\= :help remove()
เบอนัวต์

16
ทำให้แน่ใจว่าคนอย่างคุณต้องการโพสต์คำตอบเช่นนี้คือเหตุผลที่ฉันเป็นผู้ดูแล การแสดงที่ดี :)
ทิมโพสต์

1
มีปัญหาหากไม่มีช่องว่างหลังจากประโยค 4 ประโยคแรก
Reman

58

มีความสง่างามและรัดกุม Ex แก้ปัญหาคำสั่งสามารถรับได้โดยการรวม:global, :moveและ:joinคำสั่ง สมมติว่าบล็อกแรกของบรรทัดเริ่มต้นที่บรรทัดแรกของบัฟเฟอร์และเคอร์เซอร์ตั้งอยู่บนบรรทัดก่อนหน้าบรรทัดแรกของบล็อกที่สองทันทีคำสั่งจะเป็นดังนี้

:1,g/^/''+m.|-j!

สำหรับคำอธิบายโดยละเอียดของเทคนิคนี้ให้ดูคำตอบของคำถามคล้าย ๆ กัน“ Vim paste -d '' พฤติกรรมออกจากกล่อง?


E16: Invalid Range- แต่มันก็ใช้งานได้อยู่ดี เมื่อลบ1,มันจะทำงานโดยไม่มีข้อผิดพลาด
ThiefMaster

และแย่เกินไปคุณไม่สามารถตอบได้มากกว่าหนึ่งคำตอบไม่เช่นนั้นคุณจะมีเครื่องหมายสีเขียวด้วย!
ThiefMaster

3
ดีมาก! ฉันไม่รู้เกี่ยวกับ:moveและ:join!ไม่ได้''หมายความว่าอะไรเป็นอาร์กิวเมนต์ช่วง ( :help '') และอะไร+และ-หมายถึงอะไรเป็นตัวแก้ไขช่วง ( :help range) ขอบคุณ!
rampion

@ThiefMaster: คำสั่งถูกออกแบบมาให้ใช้เมื่อทั้งสองช่วงของบรรทัดที่จะเข้าร่วมอยู่ติดกันดังนั้นเคอร์เซอร์จะอยู่ที่บรรทัดสุดท้ายของบล็อกแรกที่นำหน้าบรรทัดแรกของบล็อกที่สองทันที (ดูคำตอบและคำถามที่เชื่อมโยง) คำสั่งทำงานตามที่ตั้งใจไว้แม้ว่าจำนวนบรรทัดในบล็อกจะแตกต่างกันแม้ว่าจะให้ข้อความแสดงข้อผิดพลาดก็ตาม หนึ่งสามารถระงับข้อความผิดพลาดโดยsil!การเพิ่มคำสั่ง
ไอ

2
@ib: ฉันคิดว่ามันเป็นความคิดที่ดีที่จะใส่คำอธิบายโดยละเอียดลงในคำตอบนี้ด้วย
ThiefMaster

44

ในการเข้าร่วมบล็อกของบรรทัดคุณต้องทำตามขั้นตอนต่อไปนี้:

  1. ไปที่บรรทัดที่สาม: jj
  2. เข้าสู่โหมดบล็อกภาพ: CTRL-v
  3. วางเคอร์เซอร์ไว้ที่ท้ายบรรทัด (สำคัญสำหรับบรรทัดที่มีความยาวต่างกัน): $
  4. ไปที่จุดสิ้นสุด: CTRL-END
  5. ตัดบล็อก: x
  6. ไปที่ท้ายบรรทัดแรก: kk$
  7. วางบล็อกที่นี่: p

การเคลื่อนไหวไม่ใช่สิ่งที่ดีที่สุด (ฉันไม่ใช่ผู้เชี่ยวชาญ) แต่ใช้งานได้ตามที่คุณต้องการ หวังว่าจะมีเวอร์ชั่นที่สั้นกว่านี้

นี่คือข้อกำหนดเบื้องต้นเพื่อให้เทคนิคนี้ทำงานได้ดี:

  • บรรทัดทั้งหมดของบล็อกเริ่มต้น (ในตัวอย่างในคำถามabcและdef) มีความยาวXORเท่ากัน
  • บรรทัดแรกของบล็อกเริ่มต้นนั้นยาวที่สุดและคุณไม่สนใจช่องว่างเพิ่มเติมในระหว่าง) XOR
  • บรรทัดแรกของบล็อกเริ่มต้นนั้นไม่ยาวที่สุดและคุณเว้นวรรคเพิ่มเติมจนถึงจุดสิ้นสุด

ว้าวน่าสนใจมาก! ฉันไม่เคยคิดว่ามันจะทำงานอย่างนั้น
voithos

13
สิ่งนี้ทำงานได้ตามต้องการเพียงเพราะabcและdefมีความยาวเท่ากัน การเลือกบล็อกจะเก็บเยื้องของข้อความที่ถูกลบดังนั้นหากเคอร์เซอร์อยู่ที่บรรทัดสั้น ๆ เมื่อวางข้อความบรรทัดนั้นจะแทรกระหว่างตัวอักษรที่ยาวกว่า - และเว้นวรรคต่อท้ายที่สั้นกว่าหากเคอร์เซอร์อยู่อีกต่อไป หนึ่ง.
Izkata

อันที่จริง ... มีวิธีที่จะทำอย่างถูกต้องโดยใช้คัดลอกและวางบล็อก? นี่เป็นวิธีที่ง่ายที่สุดที่จะทำ (โดยเฉพาะถ้าคุณไม่สามารถค้นหาวิธีที่ซับซ้อนกว่านี้ได้ด้วยเหตุผลบางอย่าง)
ThiefMaster

2
เช่นเดียวกับ @Izkata บอกว่าคุณจะพบปัญหาในการแทรกข้อความระหว่างบรรทัดที่ยาวขึ้น หากต้องการแก้ไขปัญหาเพียงเพิ่มช่องว่างที่ส่วนท้ายของบรรทัดแรกเพื่อให้ยาวที่สุดแล้ววางบล็อคข้อความ เมื่อเสร็จสิ้นการบีบอัดหลาย ๆ ช่องว่างให้เป็นเรื่องง่ายเหมือน:%s/ \+/ /g
Khaja Minhajuddin

1
set ve=allจะช่วยให้ดูvimdoc.sourceforge.net/htmldoc/options.html#'virtualedit '
เบน

19

นี่คือวิธีที่ฉันทำ (ด้วยเคอร์เซอร์บนบรรทัดแรก):

qama:5<CR>y$'a$p:5<CR>dd'ajq3@a

คุณต้องรู้สองสิ่ง:

  • หมายเลขบรรทัดที่บรรทัดแรกของกลุ่มที่สองเริ่มต้นขึ้น (5 ในกรณีของฉัน) และ
  • จำนวนบรรทัดในแต่ละกลุ่ม (3 ในตัวอย่างของฉัน)

นี่คือสิ่งที่เกิดขึ้น:

  • qaบันทึกทุกอย่างขึ้นต่อไปqเป็น "กันชน" aใน
  • ma สร้างเครื่องหมายบนบรรทัดปัจจุบัน
  • :5<CR> ไปที่กลุ่มถัดไป
  • y$ ดึงส่วนที่เหลือของบรรทัด
  • 'a กลับไปที่เครื่องหมายตั้งไว้ก่อนหน้า
  • $p วางที่ส่วนท้ายของบรรทัด
  • :5<CR> กลับไปที่บรรทัดแรกของกลุ่มที่สอง
  • dd ลบมัน
  • 'a กลับไปที่เครื่องหมาย
  • jq ลดลงหนึ่งบรรทัดและหยุดการบันทึก
  • 3@a ทำซ้ำการกระทำสำหรับแต่ละบรรทัด (3 ในกรณีของฉัน)

1
คุณต้องกด[Enter]หลังจาก:5ทั้งสองครั้งที่คุณพิมพ์ไม่เช่นนั้นจะไม่ทำงาน
Shawn J. Goff

1
ฉันอยู่บน gvim มีวิธีการคัดลอกและวางคำสั่งนั้นใน GVim หรือไม่? ^ V วางโดยอัตโนมัติในโหมดแทรก (ซึ่งสมเหตุสมผลแล้วนั่นคือสิ่งที่คนมักต้องการ) แม้ว่าฉันจะอยู่ในโหมดปกติ (?) ฉันพยายามแต่ที่ดูเหมือนว่าจะดำเนินการเท่านั้น:norm qama:5<CR>y$'a$p:5<CR>dd'ajq3@a q
ThiefMaster

1
ThiefMaster: ลอง:let @a="ma:5^My$'a$p:5^Mdd'aj" | normal 4@aที่^Mตัวอักษรที่พิมพ์ลงไปโดยการกดปุ่ม CTRL-V แล้วกด Enter
rampion

8

ดังที่กล่าวไว้ที่อื่นการเลือกบล็อกเป็นวิธีที่จะไป แต่คุณยังสามารถใช้ตัวแปรต่างๆของ:

:!tail -n -6 % | paste -d '\0' % - | head -n 5

วิธีนี้อาศัยบรรทัดคำสั่ง UNIX pasteยูทิลิตี้ถูกสร้างขึ้นเพื่อจัดการกับการเรียงลำดับของสายการควบรวมนี้

PASTE(1)                  BSD General Commands Manual                 PASTE(1)

NAME
     paste -- merge corresponding or subsequent lines of files

SYNOPSIS
     paste [-s] [-d list] file ...

DESCRIPTION
     The paste utility concatenates the corresponding lines of the given input files, replacing all but the last file's newline characters with a single tab character,
     and writes the resulting lines to standard output.  If end-of-file is reached on an input file while other input files still contain data, the file is treated as if
     it were an endless source of empty lines.

การใช้การเลือกบล็อกไม่ใช่วิธีเดียวที่จะไป และไม่ใช่สิ่งที่ง่ายที่สุด ที่ต้องการ ( paste -dพฤติกรรมเหมือน) สามารถดำเนินการโดยวิธีการของคำสั่งที่เป็นกลุ่มสั้นเป็นมันแสดงให้เห็นในคำตอบของฉัน
ไอ

3
นอกจากนั้นฉันอยู่บน windows เพื่อให้โซลูชันนั้นเกี่ยวข้องกับการเปิดการเชื่อมต่อ SSH กับเครื่อง linux ของฉันและวางจากตัวแก้ไขลงในเทอร์มินัลและด้านหลัง
ThiefMaster


3

ฉันไม่คิดว่าจะทำให้มันซับซ้อนเกินไป ฉันเพิ่งจะเปิด virtualedit บน
( :set virtualedit=all)
เลือกบล็อก 123 และด้านล่างทั้งหมด
วางไว้หลังคอลัมน์แรก:

abc    123
def    45
...    ...

และลบหลายช่องว่างระหว่างถึง 1 ช่องว่าง:

:%s/\s\{2,}/ /g

คำถามที่ถามจริงไม่มีช่องว่างฉันจะทำสิ่งที่ชอบ gvV:'<,'>s/\s+//g(เป็นกลุ่มควรแทรก'<,'>ให้คุณโดยอัตโนมัติดังนั้นคุณไม่จำเป็นต้องพิมพ์ด้วยตนเอง)
Ben

2

ฉันจะใช้ซ้ำที่ซับซ้อน :)

รับสิ่งนี้:

aaa
bbb
ccc

AAA
BBB
CCC

ด้วยเคอร์เซอร์บนบรรทัดแรกให้กดดังต่อไปนี้:

qa}jdd''pkJxjq

จากนั้นกด@a(และคุณสามารถใช้ในภายหลัง@@) ได้บ่อยเท่าที่ต้องการ

คุณควรท้ายด้วย:

aaaAAA
bbbBBB
cccCCC

(บวกกับการขึ้นบรรทัดใหม่)

ชี้แจง:

  • qa เริ่มบันทึกซ้ำที่ซับซ้อนมา a

  • } ข้ามไปยังบรรทัดว่างถัดไป

  • jdd ลบบรรทัดถัดไป

  • '' กลับไปที่ตำแหน่งก่อนการกระโดดครั้งสุดท้าย

  • p วางบรรทัดที่ลบแล้วภายใต้บรรทัดปัจจุบัน

  • kJ ต่อท้ายบรรทัดปัจจุบันต่อท้ายบรรทัดก่อนหน้า

  • xลบช่องว่างที่Jเพิ่มระหว่างบรรทัดที่รวมกัน คุณสามารถละเว้นสิ่งนี้หากคุณต้องการพื้นที่

  • j ไปที่บรรทัดถัดไป

  • q สิ้นสุดการบันทึกซ้ำที่ซับซ้อน

หลังจากนั้นคุณจะใช้@aเพื่อเรียกใช้การทำซ้ำที่ซับซ้อนที่เก็บไว้ในaและจากนั้นคุณสามารถใช้@@การทำซ้ำการทำซ้ำที่ซับซ้อนที่วิ่งล่าสุด


1

มีหลายวิธีในการทำสิ่งนี้ให้สำเร็จ ฉันจะรวมสองช่วงตึกของข้อความโดยใช้หนึ่งในสองวิธีต่อไปนี้

สมมติว่าบล็อกแรกอยู่ที่บรรทัดที่ 1 และบล็อกที่สองเริ่มจากบรรทัดที่ 10 โดยมีตำแหน่งเริ่มต้นของเคอร์เซอร์ที่หมายเลขบรรทัดที่ 1

(\ n หมายถึงการกดปุ่ม Enter)

1. abc
   def
   ghi        

10. 123
    456
    789

ด้วยมาโครที่ใช้คำสั่ง: คัดลอกวางและเข้าร่วม

qaqqa + 9Y \ npkJjq2 @ a10G3dd

ด้วยแมโครที่ใช้คำสั่งย้ายบรรทัดที่หมายเลขบรรทัดที่ n และเข้าร่วม

qcqqc: 10m. \ nkJjq2 @ c

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