İki satırın aynı olup olmadığını kontrol etmek için ex-komutunu mu kullanıyorsunuz?


9

Bu soruya bakıyordum ve sonra tamamen POSIX kullanan cevabımı sed nasıl uygulayabileceğimi merak ettim .ex

İşin püf noktası sed, tam olarak eşdeğer olup olmadıklarını görmek için tutma alanını desen alanı ile karşılaştırabilirken (ile G;/^\(.*\)\n\1$/{do something}), böyle bir test yapmanın hiçbir yolunu bilmiyorum ex.

Ben Vim ben olabileceğini biliyor Yilk satırı ank ve sonra yazın :2,$g/<C-r>0/diçin neredeyse ben belirterek-ama yaşıyorum yapmak ilk satır bir şey içeriyor, ancak satır olarak atılmış ediliyor beri çok basit alfanümerik metin bu, gerçekten riskli hale gelirse regex , sadece karşılaştırma için bir dize değil. (Ve ilk satırda eğik çizgi varsa, satırın geri kalanı komut olarak yorumlanır!)

Dolayısıyla myfile, ilk satırla aynı olan ancak ilk satırı silemeyen tüm satırları silmek istersem bunu nasıl yapabilirim ex? Bu nedenle, bunu nasıl yapabilirim vi?

Başka bir satırla tam olarak eşleşiyorsa bir satırı silmenin POSIX yolu var mı?

Belki de bu hayali sözdizimi gibi bir şey:

:2,$g/**lines equal to "0**/d

3
Komutu oluşturabilirsiniz, ancak biraz vimscript'e ihtiyaç duyacaktır ve muhtemelen POSIX yolu olmayacaktır::execute '2,$g/\V' . escape(getline(1), '\') . '/d'
saginaw

1
@saginaw, teşekkürler. Şimdiye kadar meydana gelen tek POSIX yaklaşımı, sadece içeriden sedbir filtre olarak kullanmak exve tüm sedyanıtımı tüm tampon üzerinde çalıştırmaktır ... ki bu elbette işe yarar (ve aslında aksine taşınabilir sed -i).
Wildcard

Haklısın ve ilk yaklaşımını <C-r>0çok iyi buluyorum . Özel karakterleri korumanız gerektiğinden, yalnızca Ex komutlarıyla daha iyisini yapabileceğinizden emin değilim. POSIX uyumlu kısıtlama olmadan ben çok nomagic anahtarını kullanacağını düşündüğünüz \Vve (hatta onun özel bir anlam tutar çünkü o zaman ters eğik çizgi koruyacağını \Vbirlikte) escape()olan 2 argüman kaçmak isteyen tüm karakterleri içeren bir dize / koruma olduğu işlevin .
saginaw

Ancak, önceki komutta eğik çizgiyi de korumayı unuttum, çünkü aynı zamanda global komut için özel bir anlamı var, bu desen sınırlayıcı. Yani doğru komut muhtemelen şöyle bir şey olurdu: :execute '2,$g/\V' . escape(getline(1), '\/') . '/d'Veya desen noktalı virgül için noktalı virgül gibi başka bir karakter kullanabilirsiniz. Bu durumda, desendeki bir eğik çizgiyi korumanız gerekmez. Şöyle bir şey verir::execute '2,$g;\V' . escape(getline(1), '\') . ';d'
saginaw

1
İkinci yaklaşımınızı sedda çok iyi buluyorum . Vim ile, genellikle belirli özel görevleri diğer programlara devredersiniz ve sedmuhtemelen bunun iyi bir örneğidir. Bu arada, sedtüm tamponunuzda koşmanıza gerek yok. Arabelleğin yalnızca bir bölümünde çalıştırmak istiyorsanız, bir aralık verebilirsiniz. Eğer 50 ile 100 arasındaki tek satırları filtrelemek istiyorsanız Örneğin, siz yazabilirsiniz: :50,100!<your sed command>.
saginaw

Yanıtlar:


3

gayret

Vim'de, yeni satır dahil herhangi bir karakteri eşleştirebilirsiniz \_.. Bunu, tüm bir çizgiyle, herhangi bir miktarda öğeyle ve ardından aynı çizgiyle eşleşen bir desen oluşturmak için kullanabilirsiniz:

/\(^.*$\)\_.*\n\1$/

Şimdi bir dosyadaki ilk satırla aynı olmayan tüm satırları silmek istiyorsunuz. İlkiyle eşleşen son satırı silme ikamesi:

:1 s/\(^.*$\)\_.*\zs\n\1$//

:globalDeğişikliğin tüm satırları silmek için yeterince kez tekrarlandığından emin olmak için kullanabilirsiniz :

:g/^/ 1s/\(^.*$\)\_.*\zs\n\1$//

POSIX eski

@saginaw, sorunuza bir yorum olarak Vim'de bunu yapmak için daha temiz bir yol gösterir, ancak yukarıdaki tekniği POSIX ex için uyarlayabiliriz.

Bunu POSIX uyumlu bir şekilde yapmak için çok satırlı eşleşmeye izin vermemeniz gerekir, ancak yine de geri başvurulardan yararlanabilirsiniz. Bu biraz fazladan çalışma gerektirir:

:g/^/ t- | s/^/@@@/ | 1t- | s/^/"/ | j! | s/^"\(.*\)@@@\1$/d/ | d x | @x

İşte arıza:

:g/^/                   for each line

t- |                    copy it above

s/^/@@@/ |              prefix it with something unique (@@@)
                        (do a search in the buffer first to make
                        sure it really is unique)

1t- |                   copy the first line above this one

s/^/"/ |                prefix with "

j! |                    join those two lines (no spaces)

s/^"\(.*\)@@@\1$/d/ |   if the part after the " and before the @@@
                        matches the part after the @@@, replace the line
                        with d

d x |                   delete the line into register x

@x                      execute it

Dolayısıyla, geçerli satır 1 satırının kopyasıysa, x kaydı içerecektir d. Bunu yürütmek geçerli satırı siler. Bir kopya değilse, "yürütüldüğünde bir işlem olmadığı için ön ekli saçmalık içerecektir , çünkü " bir yorum başlatır. Bunu başarmanın en güzel yolu mu bilmiyorum, akla ilk gelen!

İlk satır silinemez çünkü kopyalama işlemi geçici olarak satır 1'in ne olduğunu değiştirir. Bu durumda olmasaydı önek olabilir :gbir ile 2,$yerine aralığında.

Vim ve ex-vi sürüm 4.0'da test edilmiştir.

DÜZENLE

Ve bir arama deseni ( 'nomagic'set ile) oluşturmak için özel karakterlerden kaçan daha basit bir yol, bir :globalkomut oluşturur ve ardından yürütür:

:set nomagic
:1t1 | .g/^/ s#\[$^\/]#\\\&#g | s#\.\*#2,$g/^\&$/d# | d x
:@x
:set magic

Bununla birlikte, tek katmanlı olarak bunu yapamazsınız, çünkü :globalizin verilmeyen bir yuvaya sahip olacaksınız .


2

Bunu yapmanın tek POSIX yolu gibi harici bir filtre kullanmak gibi görünüyor sed.

Örneğin, dosyanızın 17. satırını yalnızca 5. satırla tamamen aynıysa ve aksi takdirde değiştirmeden bırakmak için aşağıdakileri yapabilirsiniz:

:1,17!sed '5h;17{G;/^\(.*\)\n\1$/d;s/\n.*$//;}'

( sedBurada tüm arabelleği çalıştırabilir veya yalnızca 5-17 satırlarında çalıştırabilirsiniz, ancak ilk durumda gereksiz filtreleme yapıyorsunuz - önemli değil - ve ikinci durumda sedkomutunuzda 5 ve 17 yerine 1 ve 13 numaralı sayılar . Kafa karıştırıcı.)

Yana sedsadece tek bir ileri pas yapar, ters yapmak ve 17 hattına özdeş olması halinde 5 çizgiyi silmek için kolay bir yolu yoktur. Bir süredir merak noktası olarak denedim ... bu biraz zor .


Atılım - Bunu şöyle yapabilirsiniz:

:17t 5
:5,5+!sed '1N;/^\(.*\)\n\1$/d;s/\n.*$//'

Bu aslında daha genel bir yöntem. Aynı şekilde ilk komutla aynı sonucu vermek için kullanılabilir (ve 17. satırı yalnızca 5. satırla özdeş ise silin):

:5t 17
:17,17+!sed '1N;/^\(.*\)\n\1$/d;s/\n.*$//'

37 numaralı satırla aynı kalırken dosyanın 37 satırıyla aynı olan tüm satırları silmek gibi daha geniş kullanımlar için aşağıdakileri yapabilirsiniz:

:37,$!sed '1{h;n;};G;/^\(.*\)\n\1$/d;s/\n.*$//'
:37t 0
:1,37!sed '1{h;d;};G;/^\(.*\)\n\1$/d;s/\n.*$//'

Sonuç burada iki satır aynıysa, en iyi araç kontrolü için, bir olduğunu sed , değil ex. Ancak DevSolar'ın bir yorumda belirttiği gibi , bu bir başarısızlık değildir viveya Unix araçlarıyla çalışmak exüzere tasarlanmıştır ; bu büyük bir güç.


Çok, çok daha zor: bir dosyanın sonuna bir satır eklemek, ancak satır dosyanın içinde bir yerde yoksa .
Wildcard

Bu benim cevabım gibi bir yaklaşımla yapılabilir. Yine de tek astar olacağını sanmıyorum!
Antony
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.