web-dev-qa-db-ja.com

「前」と「後」の行をgrep-invert-matchして除外する方法

次のエントリを含むテキストファイルを考えます。

aaa
bbb
ccc
ddd
eee
fff
ggg
hhh
iii

パターン(例:fff)を指定して、上記のファイルをgrepして出力を取得します。

all_lines except (pattern_matching_lines  U (B lines_before) U (A lines_after))

たとえば、B = 2およびA = 1、pattern = fffの出力は次のようになります。

aaa
bbb
ccc
hhh
iii

Grepまたは他のコマンドラインツールでこれを行うにはどうすればよいですか?


注:私が試したとき:

grep -v 'fff'  -A1 -B2 file.txt

欲しいものが手に入りません。私は代わりに得ます:

aaa
bbb
ccc
ddd
eee
fff
--
--
fff
ggg
hhh
iii
27

gnu grep-Aおよび-Bと一緒に使用して、除外するファイルの部分を正確に出力しますが、-nスイッチを追加して行番号も出力し、出力をフォーマットしてコマンドスクリプトとしてsedに渡します。それらの行を削除するには:

grep -n -A1 -B2 PATTERN infile | \
sed -n 's/^\([0-9]\{1,\}\).*/\1d/p' | \
sed -f - infile

これは、-fを介してgrepに渡されたパターンのファイルでも機能する必要があります。例:

grep -n -A1 -B2 -f patterns infile | \
sed -n 's/^\([0-9]\{1,\}\).*/\1d/p' | \
sed -f - infile

これは、3つ以上の連続する行番号を範囲に折りたたんで、たとえば2,6dの代わりに2d;3d;4d;5d;6d...入力に一致するものが数個しかない場合は、実行する価値がありません。


行の順序を保持せず、おそらく遅い他の方法:
with comm

comm -13 <(grep PATTERN -A1 -B2 <(nl -ba -nrz -s: infile) | sort) \
<(nl -ba -nrz -s: infile | sort) | cut -d: -f2-

commにはソートされた入力が必要です。つまり、ファイルが既にソートされていない限り、最終出力では行の順序が保持されないため、nlを使用してソート前に行に番号を付け、comm -13は行のみを出力します2nd FILEに固有で、次にcutnlによって追加された部分(つまり、最初のフィールドと区切り文字:)を削除します
with join

join -t: -j1 -v1 <(nl -ba -nrz -s:  infile | sort) \
<(grep PATTERN -A1 -B2 <(nl -ba -nrz -s:  infile) | sort) | cut -d: -f2-
11
don_crissti

vimを使用してもかまわない場合:

$ export PAT=fff A=1 B=2
$ vim -Nes "+g/${PAT}/.-${B},.+${A}d" '+w !tee' '+q!' foo
aaa
bbb
ccc
hhh
iii
  • -Nesは、非互換のサイレントexモードをオンにします。スクリプト作成に役立ちます。
  • +{command} ファイルに対して{command}を実行するようにvimに指示します。
  • g/${PAT}/ -/fff/に一致するすべての行。パターンにそのように扱うつもりのなかった正規表現の特殊文字が含まれている場合、これはトリッキーになります。
  • .-${B}-この行の1行上
  • .+${A}-この行の2行下(これら2つについては :he cmdline-ranges を参照)
  • d -行を削除します。
  • 次に、+w !teeは標準出力に書き込みます。
  • +q!は変更を保存せずに終了します。

変数をスキップして、パターンと数値を直接使用できます。目的を明確にするために使用しました。

9
muru

いかがですか(GNU grepおよびbashを使用):

$ grep -vFf - file.txt < <(grep -B2 -A1 'fff' file.txt)
aaa
bbb
ccc
hhh
iii

ここでは、grep -B2 -A1 'fff' file.txtによって破棄される行を見つけ、これを入力ファイルとして使用して、これらを破棄する必要な行を見つけています。

4
heemayl

一時ファイルを使用することで、十分な結果を得ることができます。

my_file=file.txt #or =$1 if in a script

#create a file with all the lines to discard, numbered
grep -n -B1 -A5 TBD "$my_file" |cut -d\  -f1|tr -d ':-'|sort > /tmp/___"$my_file"_unpair

#number all the lines
nl -nln "$my_file"|cut -d\  -f1|tr -d ':-'|sort >  /tmp/___"$my_file"_all

#join the two, creating a file with the numbers of all the lines to keep
#i.e. of those _not_ found in the "unpair" file
join -v2  /tmp/___"$my_file"_unpair /tmp/___"$my_file"_all|sort -n > /tmp/___"$my_file"_lines_to_keep

#eventually use these line numbers to extract lines from the original file
nl -nln $my_file|join - /tmp/___"$my_file"_lines_to_keep |cut -d\  -f2- > "$my_file"_clean

結果はgood-enoughです。これは、プロセスでインデントを緩めることができるためですが、それがxmlまたはインデントに依存しないファイルであれば、問題にはなりません。このスクリプトはRAMドライブを使用するため、これらの一時ファイルの書き込みと読み取りはメモリでの作業と同じくらい高速です。

1
RafDouglas

また、特定のマーカーの前のいくつかの行を除外する場合は、次のように使用できます。

awk -v nlines=2 '/Exception/ {for (i=0; i<nlines; i++) {getline}; next} 1'

(glenn jackman at https://stackoverflow.com/a/1492538

いくつかのコマンドをパイプすることで、前/後の動作を取得できます。

awk -v nlines_after=5 '/EXCEPTION/ {for (i=0; i<nlines_after; i++) {getline};print "EXCEPTION" ;next} 1' filename.txt|\
tac|\
awk -v nlines_before=1 '/EXCEPTION/ {for (i=0; i<nlines_before; i++) {getline}; next} 1'|\
tac
1
RafDouglas

一致が1つしかない場合:

A=1; B=2; n=$(grep -n 'fff' file.txt | cut -d: -f1)
head -n $((n-B-1)) file.txt ; tail -n +$((n+A+1)) file.txt

それ以外の場合(awk):

# -vA=a -vB=b -vpattern=pat must be provided
BEGIN{

    # add file again. assume single file
    ARGV[ARGC]=ARGV[ARGC-1]
    ++ARGC
}

# the same as grep -An -Bn pattern
FNR==NR && $0 ~ pattern{
    for (i = 0; i <= B; ++i)
        a[NR-i]++
    for (i = 1; i <= A; ++i)
        a[NR+i]++
}

FNR!=NR && !(FNR in a)
0
dedowsdi

これを達成する1つの方法は、おそらく最も簡単な方法は、変数を作成して以下を実行することです。

grep -v "$(grep "fff" -A1 -B2 file.txt)" file.txt

このようにして、あなたはまだあなたの構造を持っています。そして、あなたはあなたが取り除こうとしているものを1つのライナーから簡単に見ることができます。

$ grep -v "$(grep "fff" -A1 -B2 file.txt)" file.txt
aaa
bbb
ccc
hhh
iii
0
lordpavel