exコマンドを使用して、2つの行が同一かどうかを確認しますか?


9

私はこの質問を見ていて、純粋に POSIX を使用sedする私の答えを どのように実装できるのか疑問に思いました。ex

秘訣はsed、ホールドスペースとパターンスペースを比較して(とG;/^\(.*\)\n\1$/{do something})が完全に等しいかどうかを確認することは できますが、ではそのようなテストを行う方法がないことですex

Vim Yでは、最初の行をアンクしてから入力 :2,$g/<C-r>0/dして、指定したことをほぼ実行できます。ただし、最初の行に非常に単純な英数字のテキスト以外が含まれている場合、行は正規表現としてダンプされるので、これは実際に不自然になります。 、比較のための単なる文字列ではありません。(最初の行にスラッシュが含まれている場合、行の残りの部分はコマンドとして解釈されます!)

したがってmyfile、最初の行と同じであるすべての行を削除したい場合(ただし、最初の行は削除しない場合)は、どのように使用できexますか?それについて、私はそれをどのように使用できviますか?

別の行と完全に一致する行を削除するPOSIXの方法はありますか?

おそらくこの架空の構文のようなもの:

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

3
あなたはコマンドを構築することができますが、それはvimscriptを少し必要があるだろうし、それはおそらくPOSIXの方法ではないでしょう::execute '2,$g/\V' . escape(getline(1), '\') . '/d'
サギノー

1
@saginaw、ありがとう。これまでのところ、私に起こった唯一のPOSIXアプローチは、内からフィルターsedとして使用し、バッファー全体で私の回答全体を実行することです... もちろん、これ機能します(実際にはとは異なり、移植可能です)。exsedsed -i
ワイルドカード2016年

あなたは正しい、そして私はあなたの最初のアプローチが<C-r>0非常に良いと思う。特殊文字を保護する必要があるため、Exコマンドのみでより良い結果が得られるかどうかはわかりません。POSIX準拠の制約なしでは、非常にnomagicスイッチ\Vを使用し、2番目の引数がエスケープ/保護するすべての文字を含む文字列\Vであるescape()関数を使用して、バックスラッシュを保護します(これはでも特別な意味を保持するため)。 。
saginaw 2016年

ただし、前のコマンドでもフォワードスラッシュを保護するのを忘れていました。これは、スラッシュもグローバルコマンドに対して特別な意味を持つため、パターン区切り文字です。したがって、正しいコマンドはおそらく次のようになります。:execute '2,$g/\V' . escape(getline(1), '\/') . '/d'または、セミコロンのようなパターン区切り文字に別の文字を使用できます。この場合、パターンのスラッシュを保護する必要はありません。次のような結果になります:execute '2,$g;\V' . escape(getline(1), '\') . ';d'
。– saginaw

1
2番目のアプローチsedも非常に良いと思います。Vimでは、特定の特別なタスクを他のプログラムに委任することがよくありますが、これsedはおそらくその良い例です。ちなみに、sedバッファ全体で実行する必要はありません。バッファの一部でのみ実行する場合は、範囲を指定できます。たとえば、50から100までの行のみをフィルタリングする場合は、次のように入力します:50,100!<your sed command>
saginaw 2016年

回答:


3

Vim

Vimでは、改行を含む任意の文字をと照合できます\_.。これを使用して、行全体、任意の量、さらに同じ行に一致するパターンを作成できます。

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

次に、最初の行を除く、最初の行と一致するファイルのすべての行を削除します。最初に一致する最後の行を削除するための置換は、次のとおりです。

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

を使用:globalして、すべての行を削除するのに十分な回数置換が繰り返されることを確認できます。

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

POSIX ex

@saginawはVimでこれを行うためのより良い方法を質問へのコメントで示していますが、上記の手法をPOSIX exに適合させることができます。

これをPOSIX互換の方法で行うには、複数行のマッチングを禁止する必要がありますが、後方参照を引き続き使用できます。これにはいくつかの追加作業が必要です。

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

内訳は次のとおりです。

: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

したがって、現在の行が行1の複製である場合、レジスタxにはが含まれますd。実行すると現在行が削除されます。重複していない場合 は、コメントが開始される"ため、実行時に何も行われないというナンセンスが前に付いてい"ます。これがこれを達成するための最も近い方法であるかどうかはわかりませんが、最初に思いついたものです!

コピープロセスで1行目が一時的に変更されるため、最初の行を削除できない場合があります。これが当てはまらない場合:gは、2,$代わりに範囲の前にを付けることができます。

Vimとex-viバージョン4.0でテスト済み。

編集

そして、特別な文字をエスケープして検索パターン('nomagic'セット付き)を作成するより簡単な方法は、:globalコマンドを作成し、それを実行します。

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

ただし、ネストされ:globalたは許可されないため、これをワンライナーとして実行することはできません 。


2

これを行う唯一のPOSIXの方法は、などの外部フィルターを使用することsedです。

たとえば、5行目とまったく同じ場合にのみファイルの17行目を削除し、それ以外の場合は変更せずにおくには、次のようにします。

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

sedここではバッファ全体で実行することも、5〜17行目でのみ実行することもできますが、最初のケースでは不要なフィルタリングを実行します(大したことはありません)。後者の場合は、sedコマンドの5と17ではなく1と13を使用します。混乱します。)

以来sed、単一の往路を行い、逆を行うと、それは17行と同じである場合にのみ第5行を削除する簡単な方法はありません。好奇心のポイントとしてしばらく試してみましたが・・・トリッキーです。


ブレークスルー -次のように実行できます。

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

これは実際にはより一般的な方法です。同様に、次のように最初のコマンドと同じ結果を与えるために使用できます(そして、5行目と同一の場合にのみ17行目を削除します)。

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

37行目をそのままにして、37行目と同じファイルのすべての行を削除するなどのより広い用途では、次のようにすることができます。

: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.*$//'

ここでの結論は、2つの行が同一であるかどうかをチェックするための最良のツール sed、ではなくexです。しかし、DevSolarがコメント言及したように、これは、viまたはの失敗ではありません。これらは、Unixツールで動作exするように設計されています。それが大きな強みです。


はるかに難しいのは、ファイルの最後に行を挿入することです。ただし、その行がファイルのどこかにまだ存在していない場合のみです。
ワイルドカード2016年

それは私の答えと同様のアプローチで実行できるはずです。ワンライナーになるとは思いませんが!
アントニー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.