15

私はこのファイルを持っています

foo
foo bar
foo bar baz
bar baz
foo baz
baz bar
bar
baz
foo 42
foo bar 42 baz
baz 42

したい

  1. foo含む行と含まない行を選択しますbar
  2. foo含む行と含まない行を削除するbar

このために:execを使用する必要がある場所(リンクが見つかりません)を読み|ました。

次のことを試しましたが、うまくいきません

:exec "g/foo" # works
:exec "g/foo" | exec "g/bar" -- first returns lines with foo, then with bar
:exec "g/foo" | :g/bar -- same as above

そしてもちろん、行を選択できなければ実行できませんnormal dd

何か案は?

編集

報奨金に関する注意:

適切な:gコマンドと:vコマンドを使用し、条件が同じでない可能性があるため、正規表現ハックを使用しないソリューションを探しています(2つのインクルード、3つのエクスクルードを持つことができます)。

また、機能しない最後の2つの例では、行を削除するだけで機能しますが、削除せずに実行すると(つまり、選択した行を表示すると)誤った情報が返され、上記のように動作することに注意してください。

4

11 に答える 11

10

私はvimウィザードではありませんが、「fooを含み、barを含まない行を削除する」だけの場合は、次のようにする必要があります(サンプルファイルを試してみました)。

:v /bar/s/.*foo.*//

編集:実際には、これは空の行を残します。その2番目の検索パターンにオプションの改行を追加することをお勧めします。

于 2012-08-04T11:08:56.387 に答える
4

これはまだハックなことかもしれませんが、vimscriptを記述して、このための関数と特殊なコマンドを作成することができます。例えば:

command! -nargs=* -range=% G <line1>,<line2>call MultiG(<f-args>)
fun! MultiG(...) range
   let pattern = ""
   let command = ""
   for i in a:000
      if i[0] == "-"
         let pattern .= "\\(.*\\<".strpart(i,1)."\\>\\)\\@!"
      elseif i[0] == "+"
         let pattern .= "\\(.*\\<".strpart(i,1)."\\>\\)\\@="
      else
         let command = i
      endif
   endfor
   exe a:firstline.",".a:lastline."g/".pattern."/".command
endfun

これにより、「正規表現ハック」を自動化できるコマンドが作成されます。このようにあなたはすることができます

:G +foo -bar

barではなくfooですべての行を取得します。+引数がorで始まらない場合は-、コマンドの最後に追加するコマンドと見なされ:gます。だからあなたもすることができます

:G d +foo -bar

行を削除する、または

:G norm\ foXp +two\ foos -bar

あなたがあなたのスペースを脱出するならば。また、のような範囲を取り:1,3G +etc、検索語で正規表現を使用できますが、スペースをエスケープする必要があります。お役に立てれば。

于 2012-08-07T22:02:11.967 に答える
3

これは、正規表現が少し面倒になるところです。ゼロ幅の一致を使用する必要があります\(search_string\)\@=。アイテムのリストを任意の順序で照合する場合は、search_stringで開始する必要があり.*ます(したがって、照合は毎回行の先頭から開始されます)。発生しないものと一致させるには、\@!代わりにを使用します。

これらのコマンドはあなたが望むことをするべきだと思います(明確にするために#、通常ではなく、区切り文字として使用しています/):

  1. fooとbarを含む行を選択します。

    :g#\(.*foo\)\@=\(.*bar\)\@=

  2. foo、bar、bazを含む行を選択します

    :g#\(.*foo\)\@=\(.*bar\)\@=\(.*baz\)\@=

  3. fooを含み、barを含まない行を選択します

    :g#\(.*foo\)\@=\(.*bar\)\@!

  4. fooとbarを含む行を削除します

    :g#\(.*foo\)\@=\(.*bar\)\@=#d

  5. fooを含み、barを含まない行を削除します

    :g#\(.*foo\)\@=\(.*bar\)\@!#d

于 2012-08-04T10:56:32.727 に答える
3

:global正規表現は駆動するものであり、その反対であるため、正規表現を使用する意思がない限り、要件を達成することはできません:vglobal

これはハッキングではありませんが、コマンドがどのように機能するかを示しています。コマンドを操作するには式が必要です。正規表現を使用したくない場合は、それを実現できないのではないかと思います。

正規表現を使用したくない場合は、ここで回答が終了します。


私たちが心を開いたナイスガイであると仮定すると、行にが含まれ、含まれfoo ていない bar場合に真となる正規表現が必要です。

グーラッシュ王子の提案番号5はかなりありますが、後に発生した場合は機能しません。foobar

この式は仕事をします(つまり、すべての行を印刷します):

:g/^\(.*\<bar\>\)\@!\(.*\<foo\>\)\@=/

それらを削除する場合は、deleteコマンドを追加します。

:g/^\(.*\<bar\>\)\@!\(.*\<foo\>\)\@=/d

説明:

  • ^行頭から
  • \(.*\<bar\>\)ワードバー
  • \@!決して現れてはならない
  • \(.*\<foo\>\)\@=ただし、 fooという単語は行のどこかに表示される必要があります

2つのパターンを入れ替えることもできます。

:g/^\(.*\<foo\>\)\@=\(.*\<bar\>\)\@!/

同じ結果が得られます。

次の入力でテスト済み:

01      foo
02      foo bar
03      foo bar baz
04      bar baz
05      foo baz
06      baz bar
07      bar
08      baz
09      foo 42
10      foo bar 42 baz
11      42 foo baz
12      42 foo bar
13      42 bar foo
14      baz 42
15      baz foo
16      bar foo

複数の包含/除外について:

除外はパターンで作られています

\(.*\<what_to_exclude\>\)\@!

インクルードはパターンで作られています

\(.*\<what_to_include\>\)\@=

fooを含むが含まないbarすべての行を印刷するにはbaz

g/^\(.*\<bar\>\)\@!\(.*\<baz\>\)\@!\(.*\<foo\>\)\@=/

fooを含むが、42どちらbarも含まないすべての行を出力しますbaz

g/^\(.*\<bar\>\)\@!\(.*\<baz\>\)\@!\(.*\<foo\>\)\@=\(.*\<42\>\)\@=/

インクルードとエクスクルードの順序は重要ではありません。それらを混在させることもできます。

g/^\(.*\<bar\>\)\@!\(.*\<42\>\)\@=\(.*\<baz\>\)\@!\(.*\<foo\>\)\@=/
于 2012-08-07T21:04:30.717 に答える
2

のような組み合わせが機能すると思うかもしれません:g/foo/v/bar/dが、残念ながらこれは不可能であり、提案された回避策の1つに繰り返す必要があります。

ヘルプで説明されているように、舞台裏では:globalコマンドは2段階で機能します。

  • 最初に操作する線をマークし、
  • 次に、それらに対して操作を実行します。

興味深いことに、Vimソースの関連部分を調べました。ex_cmds.cex_global()で、グローバルフラグglobal_busyがビジー状態のコマンドの繰り返し実行を防止していることがわかります。

于 2012-08-04T15:35:32.810 に答える
0

組み込みのソリューションは非常に複雑に見えます。簡単な方法の1つは、LogiPatプラグインを使用することです。

Doc:http ://www.drchip.org/astronaut/vim/doc/LogiPat.txt.html

プラグイン:http ://www.drchip.org/astronaut/vim/index.html#LOGIPAT

これにより、パターンを簡単に検索できます。たとえば、barではなくfooを含む行を検索するには、次を使用します。

:LogiPat "foo"&!("bar")

これにより、論理パターンに一致するすべての行が強調表示されます(hlsを設定している場合)。そうすれば、正しい行を取得したかどうかをクロスチェックしてから、必要に応じて「n」でトラバースし、「dd」で削除できます。

于 2012-08-14T08:38:31.333 に答える
0

あなたは先を見越して否定的な見方をしたいと思っています。この記事は、あなたが達成しようとしている具体的な例を多かれ少なかれ与えます。 http://www.littletechtips.com/2009/09/vim-negative-match-using-negative-look.html

:g / foo(。* bar)\ @!/dに変更しました

これを正規表現のハックと見なす場合は、お知らせください。

于 2012-08-08T15:48:27.873 に答える
0

帽子をリングに投げます。vimのドキュメントには、再帰的なグローバルコマンドは無効であり、正規表現ソリューションはすぐにかなり厄介になると明示的に記載されているため、これはカスタム関数とコマンドの仕事だと思います。:Gコマンドを作成しました。

使用法は、で:G囲まれたパターンが続き/ます。一致してはならないパターンには、接頭辞として。が付き!ます。

:G /foo/ !/bar/ d

/foo/これにより、一致する行と一致しない行がすべて削除されます/bar/

:G /42 baz/ !/bar/ norm A$

これにより、一致し、一致しない$すべての行にが追加されます/42 baz//bar/

:G /foo/ !/bar/ !/baz/ d

これにより、一致する行と一致しない行、および一致し/foo/ない行がすべて削除されます/bar//baz/

コマンドのスクリプト:Gは次のとおりです。

function! s:ManyGlobal(args) range
  let lnums = {}
  let patterns = []
  let cmd = ''
  let threshold = 0
  let regex = '\m^\s*\(!\|v\)\=/.\{-}\%(\\\)\@<!/\s\+'

  let args = a:args
  while args =~ regex
    let pat = matchstr(args, regex)
    let pat = substitute(pat, '\m^\s*\ze/', '', '')
    call add(patterns, pat)
    let args = substitute(args, regex, '', '')
  endwhile

  if args =~ '\s*'
    let cmd = 'nu'
  else
    let cmd = args
  endif

  for p in patterns
    if p =~ '^(!\|v)'
      let op = '-'
    else
      let op = '+'
      let threshold += 1
    endif
    let marker = "let l:lnums[line('.')] = get(l:lnums, line('.'), 0)" . op . "1"
    exe a:firstline . "," . a:lastline . "g" . substitute(p, '^(!\|v)', '', '') . marker
  endfor

  let patterns = []
  for k in keys(lnums)
    if threshold == lnums[k]
      call add(patterns, '\%' . k . 'l')
    endif
  endfor

  exe a:firstline . "," . a:lastline . "g/\m" . join(patterns, '\|') . "/ " . cmd
endfunction

command! -nargs=+ -range=% G <line1>,<line2>call <SID>ManyGlobal(<q-args>)

この関数は基本的に引数を解析してから、すべての一致する行を指定された各パターンで個別にマークします。次に、適切な回数のマークが付けられた各行で、指定されたコマンドを実行します。

于 2012-08-13T18:55:03.860 に答える
0

これが、グローバルコマンドの再帰的な使用を実際にシミュレートしたものです。:g少なくとも理論的には、任意の数のコマンドを組み合わせることができます。しかし、私はあなたに警告します、それはきれいではありません!

元の問題の解決策

私はUnixプログラムnl (私と一緒に!)を使用して行番号を挿入しますが、これには純粋なVimを使用することもできます。

:%!nl -b a
:exec 'norm! qaq'|exec '.,$g/foo/d A'|exec 'norm! G"apddqaq'|exec '.,$v/bar/d'|%sort|%s/\v^\s*\d+\s*

終わり!説明と一般的な解決策を見てみましょう。

一般的な解決策

これは私が選んだアプローチです:

  • 明示的な行番号を導入する
  • ファイルの終わりをスクラッチスペースとして使用し、繰り返し操作します
  • ファイルを並べ替え、行番号を削除します

ファイルの終わりをスクラッチスペース(:g/foo/m$および同様のもの)として使用することは、かなりよく知られているトリックです(有名な回答番号1に記載されています)。:gまた、行の相対的な順序を保持することにも注意してください。これは非常に重要です。どうぞ:

準備:数直線、「アキュムレータ」レジスタをクリアます。

:%!nl
qaq

反復ビット:

  1. :executeグローバルコマンド。一致する行をアキュムレータレジスタに追加して収集し:d Aます。
  2. 収集した行をファイルの最後に貼り付けます
  3. .,$範囲(スクラッチスペース、この場合は「一致」スペース)に対して繰り返します

拡張された例を次に示します。「foo」を含む行、「bar」を含まない行、「42」を含む行を削除します(デモンストレーション用)。

:exec '.,$g/foo/d A' | exec 'norm! G"apddqaq' | exec '.,$v/bar/d A' | exec 'norm! G"apddqaq' | exec '.,$g/42/d A' | exec 'norm! G"apddqaq'
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 (this is the repeating bit)

反復ビットが終了すると、.,$便宜上、行に一致が含まれます。それら(dVG)などを削除できます。

クリーンアップ:行番号を並べ替えて削除します。

:%sort
:%s/\v^\s*\d+\s*



他の人が解決策の詳細を改善できると確信していますが、どうしても複数:gのsと:vsを1つに組み合わせる必要がある場合は、これが最も有望な解決策のようです。

于 2012-08-13T22:43:04.870 に答える
0

:gとを使用したソリューションが必要であると明確に述べられていることは承知しています:vが、これは実際に外部ツールを使用する必要がある場合の完璧な例であると確信しています。

:%!awk '\!/foo/ || /bar/'

車輪の再発明をする必要はありません。

于 2012-10-19T16:00:45.187 に答える
0

fooを含み、barを含まない行を選択します

fooを含み、barを含まない行を削除します

これはglobalsubstituteコマンドを組み合わせて実行できます。

:v/bar/s/.*foo.*//g
于 2018-03-18T06:43:36.337 に答える