sedを使用してファイル内のすべての出現を検索する


15

OPEN STEP 4.2 OSを使用しています...現在、次のsedコマンドを使用しています。

sed -n '1,/141.299.99.1/p' TESTFILE | tail -3

このコマンドは、IPが141.299.99.1のファイル内で1つのインスタンスを検索し、その前に3行も含めます。これは、IPのすべてのインスタンスとその前の3行も検索したいという例外を除きます。そして最初だけではありません。


1
してください、常にあなたのOSが含まれます。多くの場合、ソリューションは使用されているオペレーティングシステムに依存します。Unix、Linux、BSD、OSXなどを使用していますか?どのバージョンですか?
テルドン

素晴らしいポイント!Open Stepバージョン4.2の使用は非常に古く、含まれているシェルには、以下の回答に記載されている機能の多くが含まれていません。
デール14

好奇心から-OPEN STEP 4.2システムとは何ですか?今日は何に使用されますか?
トールビョールンラヴンアンデルセン

(Perlが利用可能な場合、あなたは本当にそれだけで素晴らしいことの多くを行うことができます)
するThorbjörnRavnアンデルセン

@ThorbjørnRavnAndersenたぶんこれ:en.wikipedia.org/wiki/OpenStep
Barmar 14

回答:


4

このGNU sedの例にgrep -B3基づいて、sed移動ウィンドウを使用してエミュレートする試みを次に示します(ただし、可能であればPOSIX準拠-@StéphaneChazelasに感謝します)。

sed -e '1h;2,4{;H;g;}' -e '1,3d' -e '/141\.299\.99\.1/P' -e '$!N;D' file

最初の2つの式は、複数行パターンバッファーを準備し、最初の一致の前に先行するコンテキストが3行未満であるエッジケースを処理できるようにします。中間の(正規表現一致)式は、目的の一致テキストがパターンバッファーに波及するまで、ウィンドウの上部から行を印刷します。最終的な$!N;Dスクロールが入力の終わりに到達したときを除いて一行によって窓。


-eGNU固有ではありません。POSIX /ポータブルにするために、あなたはない必要があった後、何もすることができないとして、それを}(そして、あなたが必要とする;その前に)。
ステファンシャゼル14

@StéphaneChazelasに感謝します-POSIX / portableにするには、最初のグループを次のように分割/変更する必要があると言ってい-e '1h;2,4{H;g;}' -e '1,3d'ますか?テストする非GNUシステム--posixはありません(GNU sed スイッチは気にしないようです)。
スチールドライバー14

1
はい、Linuxでは、sed従来のUnix sedの子孫である家宝ツールチェストを使用して、異なる実装をテストできます。以下のためのPOSIX / Unixの仕様はsedであるpubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html
ステファンChazelas

次のいずれでもイベントが見つかりません:N; D ':イベントが見つかりません。どこかに構文がありませんか?ありがとう!!
デール14

申し訳ありませんが、最近の編集では、最初の-e式の後に単一引用符がありませんでした。私は今それを修正しました-上記の表現でもう一度試してください。
スチールドライバー14

10

grep これのより良い仕事をします:

grep -B 3 141.299.99.1 TESTFILE

-B 3手段、各試合の前に3行を印刷します。これは--、行の各グループ間で印刷されます。それを無効にするには--no-group-separator、同様に使用します。

この-Bオプションは、GNUgrepおよびほとんどのBSDバージョン(OSXFreeBSDOpenBSDNetBSD)でもサポートされていますが、技術的には標準オプションではありません。


1
マイケル・ホーマー-ありがとう。-Bオプションはありません。他のアイデアはありますか?
デール14

@Dale GNU grepをインストールできますか?それはあなたにオプションを提供します。
バーマー14

9

ではsed、あなたは、スライディングウィンドウを行うことができます。

sed '1N;$!N;/141.299.99.1/P;D'

それはそれを行います。しかし、注意してください- 引用されbashてい! ても拡大するという非常識な動作!!! コマンド履歴からコマンド文字列に入力すると、少しおかしくなります。set +H;この場合は、コマンドの前にプレフィックスを付けてください。それを再度有効にするには(しかし、なぜ???)set -H後で行います。

もちろん、それはあなた使用している場合にのみ適用されますbash-私はあなたがそうだとは思いませんが。私はあなたが作業していることをかなり確信していますcsh- (これはたまたまbashその異常な振る舞いが履歴展開をエミュレートするシェルですが、cシェルが取った極端なものではないかもしれません)。だから、おそらく\!動作するはずです。私は願います。

それはすべて移植可能なコードです:POSIXはそのように3つの演算子を説明します:(この説明が早くも2001年に存在することを確認しただけであることに注意してください)

[2addr]N\n埋め込み\newlineを使用して元のマテリアルから追加されたマテリアルを分離し、 入力の次の行(終端のewlineを除く)をパターンスペースに追加します。現在の行番号が変更されることに注意してください。

[2addr]P パターンスペースを最初の\newlineまで、標準出力に書き込みます。

[2addr]D パターン空間の最初のセグメントを最初の\newline から削除して、次のサイクルを開始します。

そのため、最初の行では、パターンスペースに追加の行を追加するため、次のようになります。

^line 1s contents\nline 2s contents$

次に、最初の行とそれ以降のすべての行(最後の行を除く)で、パターンスペースに別の行を追加します。したがって、次のようになります。

^line 1\nline 2\nline 3$

IPアドレスがP最初の改行までリント内で見つかった場合は、ここの1行目だけです。すべてのサイクルの終わりに、あなたはD同じことを繰り返し、残りのものからやり直します。したがって、次のサイクルは次のようになります。

^line 2\nline 3\nline 4$

...等々。IPがこれら3つのいずれかで見つかった場合、最も古いものが毎回出力されます。したがって、常に3 行先だけです。

以下に簡単な例を示します。ゼロで終わるすべての番号に対して3行のバッファーが出力されます。

seq 10 52 | sed '1N;$!N;/0\(\n\|$\)/P;D'

10
18
19
20
28
29
30
38
39
40
48
49
50

それはあなたの問題よりも似ているために0\n改行または0$パターンスペースの終わりから交互に変更する必要があるため、あなたの場合よりも少し複雑です-しかし、これはアンカーを必要とするという点で微妙に異なります-パターンスペースは絶えずシフトします。

10と52の奇数のケースを使用して、アンカーが柔軟である限り、出力も柔軟であることを示しました。完全に移植性があるので、代わりにアルゴリズムに頼って同じ結果を得ることができます:

seq 10 52 | sed '1N;$!N;/[90]\n/P;D'

そして、私のウィンドウを制限しながら検索を広げます-0から9および0から3行から2行。

とにかく、あなたはアイデアを得る。


大変お世話になりました。申し訳ありませんが、検索したいファイル名はどこに置きますか?
デール14

@デール-私の悪い。sed '...' $filename。ちなみに-私はあなた自身の検索文字列からピリオドを残しましたが、実際にはパターン内のピリオドではありません-それらは任意の単一文字を表します。おそらく行う必要がありますoct\.oct\.oct\.oct彼らはので、それらをエスケープするだけの期間と一致します。
mikeserv 14

私はそれと異なる<>記号で猫を飼おうとしましたが、ここでは他のソリューションで得られるイベントが見つかりませんので、私のOSはこれらのソリューションと互換性がないのではないかと思います。
デール14

結果は-> N; /141.299.99.1/P; D ':イベントが見つかりません。
デール14

@デール-アップデートをご覧ください。役立つはずです。
mikeserv 14

4

のオプションがないことを述べたので、Perlを使用して(たとえば)4行のウィンドウをスライドさせることができます。-Bgrep

perl -ne '
    push @window,$_;
    shift @window if @window > 4;
    print @window if /141\.299\.99\.1/
' your_file

Rameshの答えは、同様のことをしていawkます。


私のバージョンのPerlがこれをサポートしているかどうかはわかりませんが、試してみます。私の質問に答えてくれてありがとう。とても感謝しています!
デール14

@デールどういたしまして。このコードが最先端のPerl機能を利用しているとは思えません。
ジョセフR. 14

4

利用可能な場合、pcregrepを使用できます。

pcregrep -M '.*\n.*\n.*\n141.299.99.1' file

PCREGREPがあるかどうかを確認します。コマンドのコンパクトさが気に入っています。あなたの時間と努力に感謝します。ありがとうございました!!!
デール14

4

シェル自体に他のgrep以外の回答と同じ基本的なアプローチを実装できます(これは、をサポートする比較的最近のシェルを想定しています=~)。

while IFS= read -r line; do 
    [[ $line =~ 141.299.99.1 ]] && printf "%s\n%s\n%s\n%s\n" $a $b $c $line;
    a=$b; b=$c; c=$line; 
done < file 

または、ファイル全体を配列に丸couldみすることもできます。

perl -e '@F=<>; 
        for($i=0;$i<=$#F;$i++){
          print $F[$i-3],$F[$i-2],$F[$i-1],$F[$i] if $F[$i]=~/141.299.99.1/
        }' file 

私のシェルは非常に古いです-スティーブジョブズオープンステップ。しかし素晴らしいアイデアとあなたの時間をありがとう!!! デール
デール14

@Dale the perlアプローチはどこでも動作します。ご使用のオペレーティングシステムについて教えてください(質問に追加してください)。
テルドン

PerlをコピーしてNotePadに入れて1行に入れると動作します!質問-マッチパターンの前に10行と言う場合、3を10に変更しますか?ありがとう!
デール14

$ F [$ iX]、ステートメントを追加することで、さらに行を追加できることがわかります。ありがとう!
デール14

4

システムがgrepコンテキストをサポートしていない場合は、代わりにack-grepを試すことができます。

ack -B 3 141.299.99.1 file

ack プログラマ向けに最適化されたgrepのようなツールです。


私はコマンドのコンパクトさが好きですが、私のシステムはmanページを見る際にackをサポートしていません。素晴らしいアイデアと時間をありがとうございました!!! デール
デール14

@デール:驚くべき!あなたのOSは何ですか?持っている場合perl、使用できますack
cuonglm

2
awk '/141.299.99.1/{for(i=1;i<=x;)print a[i++];print} {for(i=1;i<x;i++)
     a[i]=a[i+1];a[x]=$0;}'  x=3 filename

このawkソリューションでは、現在のパターンの前に常に3行が含まれる配列が使用されます。したがって、パターンが一致すると、現在のパターンとともに配列の内容が出力されます。

テスト中

-bash-3.2$ cat filename
10.0.0.1
10.0.0.2
10.0.0.3
10.0.0.4
141.299.99.1
10.0.0.5
10.0.0.6
10.0.0.7
10.0.0.8
10.0.0.9
10.0.0.10
141.299.99.1
10.0.0.11
10.0.0.12
10.0.0.13
10.0.0.14
10.0.0.15
10.0.0.16
141.299.99.1
10.0.0.17
10.0.0.18
10.0.0.19

コマンドを実行すると、出力は次のようになります。

10.0.0.2
10.0.0.3
10.0.0.4
141.299.99.1
10.0.0.8
10.0.0.9
10.0.0.10
141.299.99.1
10.0.0.14
10.0.0.15
10.0.0.16
141.299.99.1

とても詳細-ありがとうございます。試してみます。あなたの時間に感謝します!! デール
デール

テストファイルがあり、あなたのソリューションは動作します!ただし、問題は、大規模な本番ファイルで実行すると、レコード長が長すぎるために出力がコマンドで機能しないことです。このページの上部にある元のコマンドは機能しますが、1つのインスタンスのみを検出します。私はあなたの助けに感謝します。元のコマンドで複数のインスタンスを検出するためにできることはありますか?
デール

1

これらのほとんどでは、/141.299.99.1/また、(例えば)と一致します141a299q99+1141029969951ので、.正規表現で任意の文字を表すことができます。

使用すると、/141[.]299[.]99[.]1/より安全で、あなたはそれが一致していないことを確認するために正規表現全体の最初と最後に追加のコンテキストを追加することができ3141..12.104など、


1
これは良い点です-そして私も考えました。それでも、私は質問者から提供された文字列を既知のワーキングマッチとして使用し、機会が与えられたときに彼に個人的に同じことを通知しました。とにかく- これらのすべてではない-steeldriverの答えは、最初からcharの一致を引用しています。
mikeserv 14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.