web-dev-qa-db-ja.com

grepさ​​れた行の後にn番目の行を取得するにはどうすればよいですか?

次のテキストファイルを検討してください。

one 1
two 2
three 3
four 4
five 5
six 6
seven 7
eight 8

fourに一致した行の次の2行目にアクセスしたいと思います。これはラインになります

six 6

結果の行(つまり上記の行)は、さらに処理するためにパイプ処理されます(たとえば、| cut -d' ' -f2)。

Bashやその他の一般的なユーティリティでこれを行う方法はありますか? (それ以外の場合は、Pythonでスクリプトを作成します)

編集:私の特定のケースでは、fourの出現(その例を示すため)は一意であることが保証されています。しかし、そうでない場合でも、答えは興味深い拡張ケースを示しています。

7
WoJ

前の2つの答えに問題はありませんが、パターンの後の3行目を1回のsed呼び出しで見つけることができることを知っておくと思います。

sed -n "/four/ { n; n; p }" SourceData.txt

単一のプログラムが機能するため、これは複数のフィルターを実行するよりも効率的です。上記のコマンドは、「4」の各インスタンスの後に3行目を出力します。ただし、これは、一致後に2行のうちの1つで再び発生する場合を除きます(他の解決策では、このケースを想定どおりに処理しません)。また、パターンがファイルの最後または最後から2番目の行にある場合、出力は生成されません。これは、必要な場合とそうでない場合があります。

最初のインスタンスのみを照合するには:

sed -n "/four/ { n; n; p; q }" SourceData.txt

(この回答は、一致が見つかるとすぐにスキャンを終了することにより、可能な限り効率的であることに注意してください。)

このソリューションを追加するのは、sedを知る価値があり、構文がかなり不快であるにもかかわらず(正規表現は十分に悪いです!)、非常に役立つことが多いためです。この チュートリアル は良い入門書です。

16
AFH

注:この回答はもともと、OPがパターンが一度だけ出現することを明確に述べる前に書かれていました。これは、発生を見逃さないように設計されており(終わり近くでない限り、「n番目の行」はありません)、そのままにしておきます。オカレンスが1つだけであることが確実な場合、または最初のオカレンスのみを見つけたい場合は、すぐに停止し、入力ストリーム/ファイル全体を解析しない他のソリューションを検討できます無駄


このソリューションは、2行前に一致があった場合に、現在の行を出力します。前の試合の直後に発生しても別の試合を見逃さないため、他のいくつかの回答とは少し異なります。

awk -v delay=2 '{for (i=delay; i>=0; i--) t[i]=t[i-1]} /four/ {t[0]="m"} {if (t[delay]) print}'

一致するものがあれば、情報はt[0]に保存されます。各行で、t配列がシフトされます(t[-1]の値をリセットするためにt[0]t[0]にシフトすることを含みます)。配列が2行前に一致があったことを示している場合、その行が出力されます。

別の遅延(例:delay=7)を簡単に設定したり、別のパターン(例:/sda[[:digit:]]/)を使用したりできます。

10

この式を使用できます(input.txt):

grep "four" -A 2 input.txt | tail -n 1

出力は次のとおりです。

six 6

grepオプション "-A 2"は、一致した行の2行後が出力されることを示します。
そしてtailオプション "-n 1"は最後の1この結果の行が返されます。

8
zx485

POSIXで指定されたスクリプト可能なファイルエディタであるexの良い使用例のように見えます。

Sedやawkとは異なり、exは実際にはファイル編集用に設計されており、ストリーム編集ではなく、前後に移動できます。ファイル。これは、実際にはviエディターの非視覚的な形式です。

ただし、ここで重要な点は、exがアドレスをチェーンできることです。したがって、特定のテキストパターンの2行後の行を参照するのは簡単です。

これは、fourを含む2行after行に続くすべての行を出力するコマンドです。

printf '%s\n' 'g/four/+2p' | ex file.txt

UnixとLinuxのStackExchangeでexを使用して多くの回答を書きました。 これ 特に役立つかもしれないいくつかの追加の説明があります。

5
Wildcard

複数のオカレンスがあり、--で始まる行がないと仮定します。

( grep -A 2  pattern data.txt; echo '--' ) | grep -E -B1 '^--' | grep -Ev '^--'

スローモーションで:

  • ( grep -A 2 pattern data.txt; echo '--' )は、パターンと次の2行を出力し、グループ間に--行を挿入します。 echo '--'は、最後のグループの後に--が続くことを確認します。
  • grep -E -B1 '^--'直前の区切り文字と行を印刷します(私たちが探しているのは誰ですか)
  • grep -Ev '^--'は区切り文字を削除し、探している行だけを残します。
4
xenoid

あなたはすでに非常に良い答えを得ています。これは簡単にワンライナーの真の頼りになる解決策ですが、それを純粋にやりたい、または必要な場合に備えて、次のことから始めることができます。

{ while read letters _ && [ "${letters}" != four ] ; do :; done ; read && read _ number _ && echo ${number} ; } < data.txt

「4」の最初の発生のみを生成します。

ここでは、プレースホルダー変数、つまり破棄される値を受け取る変数の名前として_を使用しました。「追加のボーナス」として、| cut -f2の結果が既にスクリプトに組み込まれています。

また、上記のスクリプトは概念実証にすぎないことに注意してください。これは、例の入力データでのみ機能します。

それは次の方法で強化できます。

  • [[のように、[コマンド(=~の代わりに)とそのwhile read line && ! [[ "${line}" =~ four ]]演算子を使用して、(分割されていない)行全体を読み取り、正規表現と照合します。単純な「4」よりも複雑です。テストを無効にするために、!の前にある[[にも注意してください
  • [(または[[)コマンドをwhileループ内で2つのread&& echo ...と結合して、次のように最初の一致後に停止しないようにします。
{ while read letters _ ; do [ "${letters}" = four ] && read && read _ number _ && echo ${number} ; done ; } < data.txt

比較演算子の変更にも注意してください。ただし、隣接する可能性のある一致する行が欠落していることに注意してください。この可能性に対処するには、ある種の先読みまたはバックキャッシングが必要であり、そのためには、より高度なbash構造が必要です。

最後に、もちろん、read line && echo ${line}の代わりにread _ number _ && echo ${number}を使用して、分割されていない行全体を出力することもできます。

0
LL3