1. 概要

入力でパターンを検索する場合、grepが最初に表示されるコマンドである可能性があります。 デフォルトでは、grepコマンドは一致した行を印刷できます。 さらに、matchの前後に追加のコンテキスト行を印刷できます。

このチュートリアルでは、試合後のn行目のみを印刷する方法について説明します。

2. 問題の紹介

例を挙げて問題を理解しましょう。 まず、入力ファイルを見てみましょう。

$ cat report.txt
Performance: BAD 
- app name: weather-cache
- time: 2021-12-18 21:20:20
- resource usage: CPU: 3%, RAM: 4096MB
- description: RAM usage is too high!
- ... omitted many other lines ...
Performance: OK
- app name: database-import
- time: 2021-12-19 20:20:20
- CPU Usage: 1%, RAM Usage: 200MB
- description: everything runs well!
Performance: BAD 
- app name: weather-web
- time: 2021-12-19 21:20:20
- resource usage: CPU: 100%, RAM: 300MB
- description: CPU usage is too high!
- ... omitted many other lines ...

監視システムが実行中のすべてのアプリケーションを定期的にスキャンしてレポートを作成するとします。 report.txt は、生成されたレポートの1つです。

入力ファイルからわかるように、「パフォーマンス:OK 」ブロックと「パフォーマンス:BAD」ブロックがあります。

通常、これらの「 Performance:BAD」ブロックを詳しく調べるだけで済みます。 したがって、文字列「 Performance:BAD」が検索パターンになります。

ただし、ブロック全体を読み取る必要はありません。 たとえば、パフォーマンスの問題があるアプリケーションの名前だけを知る必要がある場合があります。これは、「パフォーマンス:BAD」行の後の最初の行です。

ただし、パフォーマンスの問題があるアプリケーションにも関心がない場合もあります。 代わりに、「リソース使用量」ステータスを知りたいのですが、これは「パフォーマンス:BAD」の次の3行目です。

したがって、一般的に「各一致の後にn行目のみを印刷する」問題であることがわかります。

このチュートリアルでは、「各一致の後に次の行のみを印刷する」から始めます。この要件は実際にはかなり頻繁に発生するためです。 その後、より一般的なケース「各一致後のn行目」に拡張します。

さらに、2つの一致する行の間の行数は常にnより大きいと想定します。

次に、最初に、各試合の後に次の行だけを印刷する方法を見てみましょう。

3. 各一致後に次の行のみを印刷する

各試合の後に次の行だけを取得するには、さまざまな方法があります。 このセクションでは、 grep sed、awkを使用する3つの簡単な方法について説明します。

次に、それらの動作を見てみましょう。

3.1. grepコマンドの使用

オプション’-A1 ‘を使用すると、grepは一致した行とその後の行を出力します。 次に、一致した行を抑制する必要があります。

これを行うには、‘grep -A1’検索結果を-vオプションを指定して別のgrepコマンドにパイプし、検索を反転します

$ grep 'Performance: BAD' --no-group-separator -A1 report.txt | grep -v 'Performance: BAD'
- app name: weather-cache
- app name: weather-web

上記の出力で見たように、期待どおりの結果が得られました。

オプション–no-group-separatorを使用して、行のグループ間のセパレーターを抑制しました。これは、デフォルトでは「」です。

3.2. sedコマンドの使用

コンパクトなsedワンライナーで問題を解決できます。 まず、コマンドを見てみましょう。

$ sed -n '/Performance: BAD/{ n; p }' report.txt
- app name: weather-cache
- app name: weather-web

上記の出力は、sedワンライナーがその役割を果たしていることを示しています。

コマンドがどのように機能するかを理解するために、コマンドをすばやく見ていきましょう。

  • -n sed に、印刷を要求しない限り、パターンスペースを自動的に印刷しないように指示します
  • / pattern/{…}–入力行が pattern と一致したときに、{…}のコマンドを実行します。
  • {n; p} –行が pattern と一致する場合、最初に次の入力行でパターンスペースを上書きし( n コマンド)、次にパターンスペースを印刷します( p ] 指図)

簡単に言えば、アイデアは次のとおりです。一致する行が見つかったら、次の行を読み取って印刷します。

3.3. awkコマンドの使用

もちろん、[X40X] awk コマンドを使用して、sedソリューションと同じアイデアを実装できます。

getline は、awkの多機能ステートメントです。 プレーンな「getline」と書くと、sedの「n」コマンドと非常によく似た動作をします–次の入力行を読み取ります。

$ awk '/Performance: BAD/{ getline; print }' report.txt 
- app name: weather-cache
- app name: weather-web

したがって、上記のawkコマンドも同様に機能します。

次に、問題を拡張して、各一致の後にn行目だけを印刷する方法を見てみましょう。

4. 各一致後にN行目のみを印刷する

この問題を解決するために、 grep sed 、およびawkコマンドを引き続き使用します。

4.1. grepコマンドの使用

grep コマンドを使用して、 n =1のケースを以前に解決しました。

grep 'pattern' --no-group-separator -A1 input | grep -v 'pattern'

-A1-Anに変更すると、「n番目」のケースが解決されると思われるかもしれません。 ただし、n> 1の場合、このアイデアは機能しなくなります。 これは、grep-Anがn+1行を出力するためです:一致した行+その後のn行

この結果をgrep-v’pattern’ にパイプすると、一致した行が抑制されます。 ただし、試合後のn行目の代わりにn行があります。

各一致の後にn番目の行を取得するには、最初に grep -An を使用して、 n +1行の各ブロックを検索します。 次に、 grep -v にパイプする代わりに、(n + 1)行ごとに出力できるコマンドにパイプします

たとえば、 awk コマンドを使用して、これを簡単に行うことができます。

$ grep 'Performance: BAD' --no-group-separator -A3 report.txt | awk 'NR % 4 == 0'
- resource usage: CPU: 3%, RAM: 4096MB
- resource usage: CPU: 100%, RAM: 300MB

上記の出力が示すように、各「 Performance:BAD」行の後に3行目があります。

また、 awk を使用して、grepの結果を後処理しました。 実際、この問題を解決するには、強力なawkだけで十分です。 これについては、後のセクションで説明します。

4.2. sedコマンドの使用

まず、「各一致後に次の行を印刷する」問題を解決したsedワンライナーをもう一度見てみましょう。

sed -n '/pattern/{ n; p }' input

上記のコマンドのコア部分は次のとおりです。行がパターンに一致すると、次の入力行( n )を取得し、印刷( p )します。

したがって、各試合の後に3行目を取得したい場合は、さらに2つの’nをワンライナーに追加できます。

$ sed -n '/Performance: BAD/{ n; n; n; p }' report.txt
- resource usage: CPU: 3%, RAM: 4096MB
- resource usage: CPU: 100%, RAM: 300MB

上記の出力でわかるように、問題は解決されました。

ただし、 sedスクリプトは変数や、ループやif-elseなどの他のスクリプト言語機能をサポートしていないため、問題の一般的なソリューションを構築するのは簡単ではありません。 ]。

たとえば、要件に「各一致の後に15行目を印刷する」と記載されている場合は、15′nと入力する必要があります。 ただし、 sed コマンドを動的にするために、シェルスクリプトなどの他のスクリプトでsedコマンドを作成できます。

4.3. awkコマンドをgetlineで使用する

sed と比較して、awkスクリプトは豊富な機能セットとCのようなフローコントロールをサポートしています。

したがって、getlineステートメントをforループでラップして、一致するたびに3行目を出力することで、前のawkコマンドを簡単に拡張できます。

$ awk -v n=3 '/Performance: BAD/ { for (i = 1; i <= n; i++) getline; print }' report.txt
- resource usage: CPU: 3%, RAM: 4096MB
- resource usage: CPU: 100%, RAM: 300MB

awk コマンドは理解しやすく、機能していることがわかります。

すでに説明したように、getlineは多機能ステートメントです。 ただし、特に初心者の場合は、getlineをデフォルトで回避することをお勧めします。

したがって、getlineを使用せずにこの問題を解決する別のawkアプローチを見てみましょう。

4.4. getlineなしでawkコマンドを使用する

awk を使用すると、変数を宣言できます。 したがって、一致する行番号を mLine などの変数に格納し、現在の行番号が( mLine + n )に等しいかどうかを確認できます。 等しい場合は、次の行を出力します。

$ awk -v n=3 '/Performance: BAD/ { mLine = NR } mLine && NR == mLine + n' report.txt 
- resource usage: CPU: 3%, RAM: 4096MB
- resource usage: CPU: 100%, RAM: 300MB

5. 結論

今日、私たちは「各試合の次の行だけを印刷する」という問題の解決から始めました。

さらに、「各一致のn行目のみを印刷する」という問題を解決するために、ソリューションを一般的なソリューションに拡張しました。

この記事では、この問題を解決するための3つのアプローチ、 grep sed 、およびawkの使用について説明しました。