grepコンテキストを行のN​​文字に制限する


31

行の長さが数千文字を超えるJSONファイルをgrepする必要があります。一致の左右に最大N文字のコンテキストを表示するようにgrepを制限するにはどうすればよいですか?一般的なLinuxパッケージで使用できる限り、grep以外のツールでも問題ありません。

これは、仮想grepスイッチ forの出力例になります。

$ grep -r foo *
hello.txt: Once upon a time a big foo came out of the woods.

$ grep -Ф 10 -r foo *
hello.txt: ime a big foo came of t



3
重複ではありません。これは約±文字ですが、推奨される代替案は約±行です。(しかし、stackoverflowへの参照は良いです。)
roaima

回答:


22

GNUの場合grep

N=10; grep -roP ".{0,$N}foo.{0,$N}" .

説明:

  • -o =>一致したもののみを印刷する
  • -P => Perlスタイルの正規表現を使用する
  • 正規表現では、$N文字に0が一致し、fooその後に文字が0で続き$Nます。

GNUがない場合grep

find . -type f -exec \
    perl -nle '
        BEGIN{$N=10}
        print if s/^.*?(.{0,$N}foo.{0,$N}).*?$/$ARGV:$1/
    ' {} \;

説明:

grepGNU grepであることに依存することができなくなったためfind、ファイルを再帰的に検索するために利用します(-rGNU のアクションgrep)。見つかったファイルごとに、Perlスニペットを実行します。

Perlスイッチ:

  • -n 行ごとにファイルを読む
  • -l 各行の最後にある改行を削除し、印刷時に元に戻します
  • -e 次の文字列をコードとして扱います

Perlスニペットは、本質的にと同じことをしていgrepます。$Nまず、変数に必要なコンテキスト文字の数を設定します。BEGIN{}手段これは、すべてのファイルのすべての行のためではない、一度実行の開始時に一度だけ実行されます。

各行に対して実行されるステートメントは、正規表現の置換が機能する場合にその行を印刷することです。

正規表現:

  • 任意の古いものなまけ一致する1行の開始時に(^.*?)が続く.{0,$N}のようgrepに続いて、ケースfoo別に続いて.{0,$N}、最終的にラインの最後まで遅延し、古いものと一致して(.*?$)。
  • これをに置き換え$ARGV:$1ます。$ARGVは、読み取られる現在のファイルの名前を保持する魔法の変数です。$1括弧が一致したもの:この場合のコンテキスト。
  • 貪欲なマッチはマッチすることfooなく失敗することなく前にすべての文字を食べるので、両端での遅延マッチが必要です(.{0,$N}ゼロ回マッチすることが許可されているため)。

1 つまり、これにより全体的な一致が失敗しない限り、何も一致させないことを好みます。要するに、できるだけ少ない文字に一致します。


いいね、ありがとう。これには、検索されたテキストだけでなく、出力全体を強調表示するという欠点がありますが| grep foo、最後に追加することで回避できます(ただし、プロセスで強調表示されているファイル名が失われます)。
dotancohen

1
@dotancohenすべてに勝つことはできないと思う:)
ジョセフR.

w / GNU grep環境変数を介して適用されるフラグに基づいて、一致する色/アプリケーションを指定できます。ですから、おそらくあなたすべてを勝ち取ることができます(約束はありません-このケースでうまくいくかどうかはわかりません)
mikeserv 14年

いい答えだ。ただ注意してください、zsh私が使用すると、例のようにN = 10を渡して動作させることができません ただしexport N=10、コマンドを実行する前に機能していれば機能します。zshで動作するように例を調整する方法はありますか?
ゲイブコプリー

それともperl -lne 'print "$ARGV: $_" for /.{0,10}foo.{0,10}/g'
ステファンChazelas

19

これを使用してみてください:

grep -r -E -o ".{0,10}wantedText.{0,10}" *

-Eは、拡張正規表現を使用することを伝えます

-oは、一致のみを印刷することを指示します

-r grepは、フォルダー内で再帰的に結果を探します

正規表現:

{0,10}は、印刷したい任意の文字数を示します

任意の文字を表します(ここでは文字自体は重要ではなく、数字だけでした)

編集:ああ、そうだね、ジョセフは私とほぼ同じ解決策を推奨している:D


ありがとうございました。本質的に同じソリューションですが、2人が個別に推奨する場合、これが最良の方法であるという自信を抱かせます。
dotancohen

Unixコミュニティは単に協力する必要があります。それが私たちです:-)
Eenoku 14年

2
それらは似ていますが、受け入れられた答えは私にとってはうまくいきませんでした(まだ長い行が生成されました)が、これはうまくいきました。N = 10のトリックは、bashシェルでは機能しません。
メーザー

cygwinの -E大幅に高速化よりもあります-P
ボブスタイン

2

取得元:http : //www.topbug.ne​​t/blog/2016/08/18/truncate-long-matching-lines-of-grep-a-solution-that-preserves-color/ および https:// stackoverflow。 com / a / 39029954/1150462

提案されたアプローチ".{0,10}<original pattern>.{0,10}"は、強調表示の色がしばしばめちゃくちゃになることを除いて、完全に良好です。同様の出力を持つスクリプトを作成しましたが、色も保持されています。

#!/bin/bash

# Usage:
#   grepl PATTERN [FILE]

# how many characters around the searching keyword should be shown?
context_length=10

# What is the length of the control character for the color before and after the matching string?
# This is mostly determined by the environmental variable GREP_COLORS.
control_length_before=$(($(echo a | grep --color=always a | cut -d a -f '1' | wc -c)-1))
control_length_after=$(($(echo a | grep --color=always a | cut -d a -f '2' | wc -c)-1))

grep -E --color=always "$1" $2 | grep --color=none -oE ".{0,$(($control_length_before + $context_length))}$1.{0,$(($control_length_after + $context_length))}"

スクリプトは次のように保存されていると仮定するとgrepl、その後、grepl pattern file_with_long_lines一致する行を表示しますが、一致する文字列の周りのみ10文字でなければなりません。


0

フラグを使用cutしてstdoutをパイピングし-bます。grepの出力には、1行につき1〜400バイトのみを指示できます。

grep "foobar" * | cut -b 1-400
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.