web-dev-qa-db-ja.com

g / re / pを使用した改行の置換がVimの他のすべての行にのみ適用されるのはなぜですか?

考えてみましょう:

foo
bar
....
bar
baz

次に:

:g/bar/s/\n/\r\r/g

予期せず返されます:

foo
bar

bar
bar

bar
bar

...
baz

barごとに改行が必要な場合。まだ:

:g/bar/p

戻り値:

bar
bar
...
bar

予想どおり、すべてのbarsの間に改行がある場合、期待どおりの改行が得られます。barを含むすべての行の後に改行が追加されます。私は次の方法で問題を解決しました:

:%s/\(bar.*\)\n/\1\r\r/g

しかし、私は本当に何が起こっているのかをよりよく理解したいと思います。

4
gvkv

この詳細レベルで何が起こっているのかを理解するには、ソースコードに手を伸ばす必要があると思います。したがって、_vim-7.1.314/src/ex_cmds.c_(ex_global()の定義の先頭にコメント)を引用します。

これは2つのパスで実装されます。最初にファイルをスキャンしてパターンを探し、一致する(一致しない)各行にマークを設定します。次に、マークのある行ごとにコマンドを実行します。行を削除した後、次の一致を検索する場所がわからないため、これが必要です。

したがって、_:g_ 'edコマンドで行を削除すると、この行のマークも消えます。 _:g_ 'edコマンドで行を作成した場合、その行はマークされないため、コマンドは実行されません。この例では、3行目が2行目と結合されているため、3行目のマークが消え、2行目の次のマークが4行目に最初に配置されたマークになります。

また、_ex_global_は_global_busy_変数を設定します。これにより、_:s_を含むいくつかのコマンドの動作がわずかに異なります。ただし、ここでは違いは関係ないと思います。

Gilles はあなたの「なぜ?」と答えましたが、あなたがやりたいことをするもっと簡単な方法に興味があるのではないかと思いました。

barごとに改行が必要です。

行の終了文字を置き換える代わりに(したがって、Gillesの回答に従って、行を結合して内部マークを失う)、一致する各行の終わりの直前に空白行を追加します。 $を使用して、行の終わりの直前で幅ゼロの一致を取得し、そこに改行を挿入します(パターンは行ごとに一度だけ一致するため、/g修飾子も必要ありません)。

:g/bar/s/$/\r/
3
Chris Johnsen