web-dev-qa-db-ja.com

行内のパターンのn番目のオカレンスまで(およびそれを含む)を印刷します

ファイル内に複数の行similarがあるとしましょう。

Turbo is a cat. cats are good. cats are not dog.
Coco is a black cat. cats are furry. cats are not dog.

さて、grepすべての^.*catただし、単語catが最初(またはn番目)に出現するまでキャプチャすることについて特に言及したいと思います。
必要な出力:

Turbo is a cat
Coco is a black cat
*blah is a so and so cat*

どうすればgrepできますか?

PS:grep(またはそのその他のバリエーション)のみを使用して回答したいと思います。

PS:grepしたくない^.*cat.次に、任意の操作を実行して"。"を削除します。一般的な答えが欲しいです。

3
Krishna Gupta

POSIX grepを使用すると、行全体を印刷するか、行の内容をまったく印刷しないかを選択できます。線を変換する場合は、sedやawkなどの別のツールを使用する必要があります。 catが最初に出現するまで印刷するには:

sed -n 's/cat.*/cat/'
awk 'sub(/cat.*/,"")'

[〜#〜] n [〜#〜]番目のオカレンスまでの印刷はより複雑です。

sed -n 's/cat/&\
/3; T; P'
awk 'gsub(/cat/,"&\n") >= 3 {split($0, a, "\n"); printf "%s%s%s\n", a[1], a[2], a[3]}'

GNU grepを使用すると、-oオプションを使用して、行の一致した部分のみを印刷できます。 -Pオプションを使用してPerl構文をアクティブにし、 貪欲でない量指定子 が使用できるようにします。

grep -P -o '^(.*?cat){1}'

中括弧内の数字を、最後に出現したcatの数字nに置き換えます。

拡張正規表現(-E)で同じことを表現することは可能ですが、これには複雑な正規表現が必要です。そのサイズは、カウントするパーツのサイズ(ここではcat)で指数関数的です。

grepは、指定された正規表現に基づいて行のみを選択し、それらを出力します。

私はあなたが出力ラインをパイプし、仕事をするために追加のコマンドを使用することを余儀なくされていると思います。

通常、sedまたはawkを使用して、grepなしでジョブを実行します。これは、行の選択と文字列の置換の両方が可能なためです。

awkを使用した以下の解決策があります。

awk -v Word=cat -v n=2 'BEGIN {wordlength=length(Word);} {line=$0;outputline="";position=index(line,Word);for (i=1;position>0 && i<=n; i++) { outputline=outputline substr(line,1,position+wordlength-1);line=substr(line,position+wordlength);position=index(line,Word);  } if (i!=1) {print outputline;}}'

検索する文字列にWordを設定し、必要な出現回数にnを設定する必要があります。

テスト:

$ awk -v Word=cat -v n=2 'BEGIN {wordlength=length(Word);} {line=$0;outputline="";position=index(line,Word);for (i=1;position>0 && i<=n; i++) { outputline=outputline substr(line,1,position+wordlength-1);line=substr(line,position+wordlength);position=index(line,Word);  } if (i!=1) {print outputline;}}' file
Turbo is a cat. cat
Coco is a black cat. cat
1
Jay jargot

これがsedソリューションです(たとえば、2番目のオカレンスまで印刷します。2をあなたの番号に置き換えます):

sed -n 's/cat/&\
/2
t print
d
:print
P' infile

これにより、-nによる自動印刷が無効になり、2番目に出現するcatcat +改行文字に置き換えようとします。置換が成功すると、:printに分岐し、改行までPrintsします。それ以外の場合、行はdeletedになります。


gnu sedを使用すると、ワンライナーとして書くことができます(たとえば、5回目までの印刷):

sed -n 's/cat/&\n/5;tt;d;:t;P' infile
0
don_crissti