Vim'de birden çok satırı (iki blok) birleştir


323

Vim'de iki satır bloğu birleştirmek istiyorum, yani satırları alıp satırlara n..meklemek istiyorum a..b. Bir sözde kod açıklaması tercih ediyorsanız:[a[i] + b[i] for i in min(len(a), len(b))]

Misal:

abc
def
...

123
45
...

olmalı

abc123
def45

Elle kopyala ve yapıştır yapmadan bunu yapmanın güzel bir yolu var mı?


Yani ... alternatif hatlara katılmak ister misin? Yani, join xile katılmak ister x+2misin?
larsks

1
Hayır, iki ayrı bloğum var. [a[i] + b[i] for i in min(len(a), len(b))]
Sahte

Yanıtlar:


880

Tüm bunları kesinlikle tek bir kopyala / yapıştır ile yapabilirsiniz (blok modu seçimini kullanarak), ancak tahminimce bu istediğiniz şey değil.

Bunu sadece Ex komutlarıyla yapmak istiyorsanız

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

dönüşecek

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

içine

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

GÜNCELLEME: Bu çok upvotes ile cevap daha kapsamlı bir açıklamayı hak ediyor.

Vim'de, |birden çok Ex komutunu zincirlemek için dikey çizgi karakterini ( ) kullanabilirsiniz, böylece yukarıdakine eşdeğerdir

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

Birçok Ex komutları bir önek argüman olarak çizgilerin bir dizi kabul - Yukarıdaki durumda 5,8önce delve 1,4daha önce s///belirttiğiniz komutları üzerinde faaliyet gösterdikleri çizgiler.

delverilen satırları siler. Bir kayıt argümanı alabilir, ancak biri verilmediğinde, @"normal modda silme gibi satırları isimsiz yazmacıya atar . let l=split(@")daha sonra, varsayılan sınırlayıcı: boşluk kullanarak silinen satırları bir listeye böler. Silinen satırlarda boşluk bulunan girdide düzgün çalışmak için, örneğin:

more than 
hour 
our 
never 
ever
after
work is
over
~

iki liste elemanları içine olmaktan bölünmesini "eseridir" önlemek için, farklı bir sınırlayıcı belirtmek gerekir: let l=split(@","\n").

Son olarak, ikamede s/$/\=remove(l,0)/, her satırın ( $) sonunu ifadenin değeri ile değiştiririz remove(l,0). remove(l,0)listeyi değiştirir, lilk öğesini siler ve döndürür. Bu, silinen satırları okuduğumuz sıraya göre değiştirmemizi sağlar. Bunun yerine, silinen satırları ters sırayla kullanarak değiştirebiliriz remove(l,-1).


1
hmm ... enter tuşuna sadece bir kez basmam gerekiyor. Ve iki yarım arasına boşluk bırakmaz. Çizgilerde bir miktar boşluk varsa ("çalış" örneğinde olduğu gibi), yine de orada olacaktır. Bunun s/\s*$/yerine kullanarak herhangi bir sondaki boşluktan kurtulabilirsiniz s/$/.
rampion

1
Açıklama için teşekkürler. :sil5,8del | let l=split(@") | sil1,4s/$/\=remove(l,0)/ | call histdel("/", -1) | nohlsçalıştırdıktan sonra arama geçmişini temizlediğinden daha iyi görünüyor. Ve enter tuşuna basmamı gerektiren "x daha fazla / daha az satır" mesajı göstermiyor.
ThiefMaster

11
O cevap için tam vim başvuru isterseniz: :help range, :help :d, :help :let, :help split(), :help :s, :help :s\=, :help remove().
Benoit

16
Senin gibi insanların böyle cevaplar göndermek istediğinden emin olmak, bu yüzden moderatör oldum. İyi gösteri :)
Tim Post

1
İlk 4 cümlenin ardından boşluk bırakılmazsa bir sorun vardır.
Reman

58

Sorunu çözme Şık ve özlü Ex komut birleştirerek elde edilebilir :global, :moveve :joinkomutları. İlk satır bloğunun tamponun ilk satırında başladığını ve imlecin ikinci bloğun ilk satırından hemen önceki satırda bulunduğunu varsayarsak, komut aşağıdaki gibidir.

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

Bu tekniğin ayrıntılı açıklaması için, benzer bir soruya “ Vim macunu -d” davranışı cevabına bakın ? ”.


E16: Invalid Range- ama yine de çalışıyor. Kaldırırken 1,hatasız çalışır.
ThiefMaster

Ve çok kötü birden fazla yanıtı kabul edemezsiniz - aksi takdirde sizinkinin de yeşil işareti olur!
ThiefMaster

3
Çok hoş! Ben bilmiyordum :moveve :join!ne de neyi ''bir dizi argüman (olarak demek :help '') ve ne +ve -aralık değiştiricileri olarak geliyordu ( :help range). Teşekkürler!
rampion

@ThiefMaster: Komut, birleştirilecek iki satır aralığı birbirine bitişik olduğunda kullanılmak üzere tasarlanmıştır, böylece imleç başlangıçta ikinci bloğun ilk satırından hemen önce gelen ilk bloğun son satırında bulunur. (Bağlantılı cevap ve soruya bakın.) Bu durumda bir hata mesajı vermesine rağmen, bloklardaki satır sayısı farklı olsa bile komut amaçlandığı şekilde çalışır. Komuta sil!geçerek hata mesajlarını bastırabilirsiniz .
ib.

2
@ib .: Ben de detaylı açıklamayı bu cevaba koymak da iyi bir fikir olacaktır.
ThiefMaster

44

Hat bloklarını birleştirmek için aşağıdaki adımları uygulamanız gerekir:

  1. Üçüncü satıra gidin: jj
  2. Görsel blok moduna girin: CTRL-v
  3. İmleci çizginin sonuna tutturun (farklı uzunluktaki çizgiler için önemlidir): $
  4. Sona git: CTRL-END
  5. Bloğu kesin: x
  6. İlk satırın sonuna git: kk$
  7. Bloğu buraya yapıştırın: p

Hareket en iyisi değil (ben uzman değilim), ama istediğin gibi çalışıyor. Umarım daha kısa bir versiyonu olacak.

İşte önkoşullar bu yüzden bu teknik iyi çalışıyor:

  • Başlangıç ​​bloğunun tüm satırları (sorudaki örnekte abcve def) aynı uzunluktadır XOR
  • başlangıç ​​bloğunun ilk satırı en uzundur ve aradaki ek boşluklarla ilgilenmezsiniz) XOR
  • Başlangıç ​​bloğunun ilk satırı en uzun değil ve sonuna kadar ek boşluklar var.

Woah, bu ilginç! Asla bu şekilde çalışacağını düşünmezdim.
voithos

13
Bu sadece istendiği gibi çalışır abcve defaynı uzunluktadır. Blok seçimi, silinen metnin girintilerini koruyacaktır, bu nedenle imleç metni yerleştirirken kısa bir çizgi üzerindeyse, satırlar daha uzun olanlara harfler arasına eklenir - ve imleç daha uzunsa daha kısa olanlara eklenen boşluklar bir.
Izkata

Gerçekten de ... Blok kopyala yapıştır özelliğini kullanarak bunu yapmanın bir yolu var mı? Sonuçta bunu yapmanın en kolay yolu (özellikle bir nedenden dolayı burada daha karmaşık yolları arayamazsanız).
ThiefMaster

2
@Izkata'nın dediği gibi, daha uzun satırlar arasına metin yerleştirme sorunuyla karşılaşacaksınız. Bu sorunu çözmek için, ilk satırın sonuna en uzun olacak şekilde daha fazla boşluk ekleyin ve ardından metin bloğunuzu yapıştırın. Bir kez bittiğinde, birden fazla alanı bir birine sıkıştırmak kadar basit:%s/ \+/ /g
Khaja Minhajuddin

1
set ve=allyardımcı olmalı, bkz. vimdoc.sourceforge.net/htmldoc/options.html#'virtualedit '
Ben

19

Bunu nasıl yapacağım (ilk satırda imleç ile):

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

İki şeyi bilmeniz gerekir:

  • İkinci grubun ilk satırının başladığı satır numarası (benim durumumda 5) ve
  • her gruptaki satır sayısı (örneğimde 3).

Neler oluyor:

  • qasonraki her şeyi qbir "tampon" içine kaydeder a.
  • ma geçerli satırda bir işaret oluşturur.
  • :5<CR> sonraki gruba gider.
  • y$ çizginin geri kalanını sorar.
  • 'a daha önce ayarlanan işarete geri döner.
  • $p satır sonundaki macunlar.
  • :5<CR> ikinci grubun ilk satırına döner.
  • dd siler.
  • 'a işaretine geri döner.
  • jq bir satır aşağı iner ve kaydı durdurur.
  • 3@a her satır için eylemi tekrarlar (benim durumumda 3)

1
İki kez [Enter]yazdıktan sonra basmanız gerekir, :5aksi takdirde çalışmaz.
Shawn J. Goff

1
Gvim'deyim. GVim'de bu komutu kopyalayıp yapıştırmanın bir yolu var mı? ^ V, şu anda normal (?) Modda olsam bile ekleme moduna otomatik olarak yapışır (bu, insanların genellikle istediği şeydir). Denedim :norm qama:5<CR>y$'a$p:5<CR>dd'ajq3@aama bu sadece idam gibi görünüyor q.
ThiefMaster

1
ThiefMaster: dene :let @a="ma:5^My$'a$p:5^Mdd'aj" | normal 4@a, ^Mkarakterler CTRL-V daha sonra Enter tuşuna basarak yazılır.
rampion

8

Başka bir yerde belirtildiği gibi, blok seçimi gitmek için bir yoldur. Ancak aşağıdakilerin herhangi bir varyantını da kullanabilirsiniz:

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

Bu yöntem UNIX komut satırına dayanır. pasteYarar birleştiren hatların bu tür işlemek için oluşturuldu.

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.

Blok seçimini kullanmak tek yol değildir. Ne de en basit olanı. İstenen ( paste -do gösterildiği gibi benzeri) davranışı, kısa Vim komutu vasıtasıyla uygulanabilir Cevabıma .
ib.

3
Bunun yanı sıra, windows'dayım, böylece çözüm linux makineme bir SSH bağlantısı açmayı ve editörden terminale ve arkaya yapışmayı içerecektir.
ThiefMaster

3

Örnek veriler, rampion ile aynıdır.

:1,4s/$/\=getline(line('.')+4)/ | 5,8d

3

Bunu çok karmaşık hale getirmeyi düşünmezdim. Ben sadece
( :set virtualedit=all)
123 bloğunu seçin ve aşağıdakiler için virtualedit ayarlayacağım .
İlk sütundan sonra koyun:

abc    123
def    45
...    ...

ve 1 boşluk arasındaki çoklu alanı kaldırın:

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

Soru aslında boşluk istemiyor, ben böyle bir şey yapardım gvV:'<,'>s/\s+//g(vim '<,'>sizin için otomatik olarak eklemelidir, bu yüzden elle yazmanız gerekmez).
Ben

2

Karmaşık tekrarlar kullanırdım :)

Bu göz önüne alındığında:

aaa
bbb
ccc

AAA
BBB
CCC

İmleç ilk satırdayken aşağıdakilere basın:

qa}jdd''pkJxjq

ve sonra düğmesine @a(ve daha sonra kullanabilirsiniz @@) gerektiği kadar basın.

Şununla bitirmelisiniz:

aaaAAA
bbbBBB
cccCCC

(Artı bir yeni satır.)

Açıklama:

  • qa karmaşık bir tekrar kaydetmeye başlar a

  • } sonraki boş satıra atlar

  • jdd sonraki satırı siler

  • '' son atlamadan önceki konuma geri döner

  • p silinen satırı geçerli satırın altına yapıştır

  • kJ geçerli satırı bir öncekinin sonuna ekle

  • xJbirleştirilmiş çizgiler arasına eklenen boşluğu silme ; alan istiyorsanız bunu atlayabilirsiniz

  • j sonraki satıra git

  • q Karmaşık tekrar kaydını sonlandır

Bundan sonra, @asaklanan karmaşık tekrarı çalıştırmak için kullanabilirsiniz ave daha sonra @@son çalıştırılan karmaşık tekrarı yeniden çalıştırmak için kullanabilirsiniz .


1

Bunu başarmanın birçok yolu olabilir. Aşağıdaki iki yöntemden herhangi birini kullanarak iki metin bloğunu birleştireceğim.

birinci bloğun 1. satırda olduğunu ve 2. bloğun 10. satırdan başladığını ve imlecin başlangıç ​​konumu 1 numaralı satırda olduğunu varsayalım.

(\ n, enter tuşuna basmak anlamına gelir.)

1. abc
   def
   ghi        

10. 123
    456
    789

şu komutları kullanarak bir makro ile: copy, paste ve join.

qaqqa + 9y \ npkJjq2 @ a10G3dd

komutları kullanan bir makro ile bir satırı n. satır numarasına taşıyın ve birleştirin.

qcqqc: 10m. \ nkJjq2 @ c

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.