grepの-A -B -Cスイッチ(前後に数行を出力する)に代わるものはありますか?


10
grep -A 2 -B 3 

grep文字列の後に2行印刷し、その前に3行印刷します。

grep -C 3

前に3行と後の3行を印刷します

残念ながら、grep私が使用しているものはこれらのオプションをサポートしていません。これをシミュレートするために使用できる代替コマンドまたはスクリプトはありますか?sed/ awk/ perl/ shellスクリプトを使用していますか?


+1 -Cスイッチがあることを知りませんでした。
Lazer、2011

1
GNUのgrepをインストールします。より一般的には、新しいSunマシンがここに到着したとき、セットアップの最初のステップは誰かが呼ばれたものGNU > /usr/localでした。GNUプログラムには非常に便利な拡張機能がたくさんあり、恣意的な制限を回避するように設計されています(ただし、サイズとパフォーマンスは高くつきます)。多くの所有システムには、GNUやその他のツールを備えた「非公式な」パッケージリポジトリがあります。彼らがベンダーによって管理されている場合でも、「パートナー」はそれらについて教えてくれません...
フォンブランド'23年

回答:


6

それを行うには、少し醜い方法があります

grep -v pattern file >file.tmp; diff -c file.tmp file

または交換-c-C NUMするためにNUMコンテキストのライン。ただし、追加の出力が生成されます。(もしあなたdiff-u/をサポートしていれば、-U NUMよりクリーンになります。)

/ / diffがない場合でも、それを行う方法はありますが、かなり醜いです。一方、サポートさえしていないシステムはおそらくPerlも持っていません。-c-C-udiff-c


これはかっこいい、魅力のように機能しますが、Windowsで生成されたファイルで機能させるために-bitwオプションを使用する必要がありました。
Prashant Bhate

stdinをdiffに送信し、一時grep -v pattern file | diff -c - file
ファイル

5

ackはPerlのみを必要とし-A、grepのように機能する、、-Bおよび-Cオプションが含まれます。grepの代わりにPerlの正規表現構文を使用し、検索するファイルを選択する方法はかなり異なります。この-fオプションを使用するときに試してみることをお勧めします(実際には何も検索せずに、検索するファイルを出力します)。

非コアモジュールを必要としない単一のスクリプトとしてインストールできます。それを自分の~/binディレクトリ(またはPATH上の書き込みアクセス権を持つ他の場所)にドロップし、それがchmod実行可能であることを確認してください。


そのプロダクションボックスと、残念ながら、何かをインストールする十分な特権がなく、危険を冒すことはできませんが、このヒントのおかげで、インストールして自宅のラップトップで試します
Prashant Bhate

@Prashant、ack自分で使用するためにrootをインストールする必要はありません。
cjm

はい、それでも私はそこでは使用できませんが、このスクリプトが私の〜/ binに永久に残ることは確かです:)
Prashant Bhate

@Prashant:どうして使えないの?これは単なるperlスクリプトです。
2011

1
そのPRODUCTIONボックスは、特別な許可を取得する必要があります。何かがうまくいかないと頭に
浮かび

5

この単純なperlスクリプトはgrep -Aある程度エミュレートします

#!/usr/bin/perl

$pattern=shift; #patthern to search
$lines=shift; # number of lines to print

$n = 0;
while (<>) {
  $n = $lines if /$pattern/; # reset counting
  if ($n) { print; $n-- } # print if within
  $n = 0 if eof; # don't leak across file boundaries
}

スクリプトを読みやすく、使いやすくするために、使用方法の説明を追加することができます;)

USAGE:    $./grep-A.pl <pattern> <numLines> <filename> 

これを実行するには、どのバージョンのperlが必要ですか?
Prashant Bhate

私はv5.10.1を使用していますが、最近はperl 5がかなり一般的だと思います。
Vijay Anant

ya 5.8.8とうまくいきましたが、-Bの機能を実行するスクリプトが必要です
Prashant Bhate

良い。ただし、引数の順序は入れ替えます。grep-A 3 fooよりもはるかに自然に見えますgrep-A foo 3。:-)
musiphil

3

GNU grepまたはAckをインストールするだけです(Perlで作成され、GNU grepのオプションの多くを理解しています)。

標準のツールと少しのスクリプトにこだわる場合は、GNU grep とオプションの動作をエミュレートするawkスクリプトを次に示します。最小限のテスト。-A-B

#!/bin/sh
# grep-ac: a grep-like awk script
# Arguments: pattern = awk regexp to search for
#            before = number of lines to print before a match
#            after = number of lines to print after a match
{ "exec" "awk" "-f" "$0" "$@"; }
# The array h contains the history of lines that haven't been printed
# but are eligible for being "before" lines.
# The variable until contains the number of the last "after" line to print.
match($0, pattern) {   # the current line matches
    for (i in h) {
        print h[i];    # print each remaining before line
        delete h[i];   # delete each line as it's printed
    }
    until=NR+after;    # record the last after line to print
}
{
    if (NR<=until) print $0;    # from a match to its last after line: print
    else h[NR]=$0;              # after that: save in history
    delete h[NR-before];        # remove line too old to be a before line
}
END {exit !until}               # exit status: 0 if there was a match, else 1

実行grep-ac -vpattern=PATTERN -vbefore=NBEFORE -vafter=NAFTER場所PATTERNを検索するためのパターンである(拡張正規表現少ないとAWKの追加)、およびNBEFORENAFTER(0をデフォルト)は、それぞれ前と一致した後に印刷するライン数です。例:

<input_file grep-ac -vbefore=2 -vpattern='foo *bar'

データを配列に格納するソリューションは問題外です...先に述べたように、ファイルサイズは非常に大きく、オーバーフローする可能性があります。また、このシステムのawkでは、ファイルサイズが3000バイトを超えることはできません。
Prashant Bhate

2
@Prashant:私はあなたの反対を理解していません。このスクリプトは、前の行になる資格がなくなった行を削除します。awkが特殊目的のプログラムよりもオーバーヘッドが高くなる可能性がある(ただし、Perlよりも少なく、これも考慮している)場合を除いて、要件に基づいて本質的に必要とされるよりも多くのメモリを使用しません。ファイルの合計サイズは完全に無関係です。
Gilles「SO-邪悪なこと

2
{ "exec" "awk" "-f" "$0" "$@"; }:シバン行解析の制限を回避する非常に気の利いた方法。
dubiousjim 2012年

2

-Bをエミュレートするのは非常に難しいことがわかります。これは、一致する行が直接続いている場合に発生する問題のためです。これにより、あらゆる種類のシングルパススルーファイルスキャンを使用できなくなります。

私は次の概算で遊んでいる間にこれに気づきました:

perl -pe 'if(/search_term/) {print foreach @A; print ">"; $B=4}; shift @A if push(@A, $_)>7; $_ = "" unless ($B-- > 0);' target_file

これは、grep -A7 -B3とほぼ同じように正しく機能しますが、最初の段落で説明した注意事項があります。

この問題の代替(単一ファイル)ソリューションは、perlを使用してコマンド文字列をフィードすることです:

sed -n `perl -pe '$_=(/search_term/?sprintf("%d,%dp;", $.-3,$.+4):"")' file` file

かなり長いonelinerですが、このファイルは非常に大きいため、この場合、行を配列にプッシュすることは悪い考えです。
Prashant Bhate

shift @A if push(@A,$_)>7;ビットは、最大サイズ7の周囲の配列を保持します。(これが-Aパラメータです)。2番目のオプションは、信じられないほど小さなファイルを保持します(sedの外層なしでperlを実行して、そこで何が生成されるかを確認します)が、ファイルを2回読み取ります。
user455 2011

0

を使用sedすると、最初に一致する行の行番号を取得し、whileループで特定の行番号をデクリメントおよびインクリメントしてsed -n "n1,n2p"から、先頭の(n1)および末尾の(n2)コンテキストの行を出力することができます(seduser455が提案する代替方法と同様)。ただし、多くの読み取りプロセスはパフォーマンスに影響を与える可能性があります。

ed一致した行の前後の行を直接参照できますが、指定された行範囲が存在しない場合は失敗します。たとえば、一致する行は行番号2ですが、5つの一致前行を出力する必要があります。edしたがって、これを使用するには、最初と最後に適切な数の(空の)行を追加する必要があります。(edただし、巨大なファイルは適切なツールではない可能性があります。bfs-大きなファイルスキャナーを参照してください)。

# sample code to match lines with number 5 plus previous & following line
# (using Bash)
printf '%s\n' {1..20} > num.txt

# sed
sed -n '/5/=' num.txt | while read num; do
   n1=$((num - 1))
   n2=$((num + 1))
   [[ $n1 -lt 1 ]] && n1=1
   sed -n "${n1},${n2}p" num.txt
   echo --
done | sed -e '${/^--$/d;}'

# ed
cat <<-'EOF' | ed -s num.txt | sed -e $'N;N;a\\\n--' | sed -e '${/^--$/d;}'
H
0i
beginning: added line one
.
$a
end: added line one
.
,g/5/km\
'm-1,'m+1p
q
EOF
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.