1

文字列を検索し(4320101を使用できます)、文字列の上に20行印刷し、文字列が見つかるまでこの後に印刷できるようにする必要があります

例えば:

Random text I do not want or blank line
16 Apr 2013 00:14:15
id="4320101"
</eventUpdate>
Random text I do not want or blank line

次の結果をファイルに出力したいだけです。

16 Apr 2013 00:14:15
id="4320101"
</eventUpdate>

必要なファイルには、これらのテキスト グループの例が複数あります。

私はこれを以下で使用してみました:

cat filename | grep "</eventUpdate>" -A 20 4320101 -B 100 > greptest.txt

ただし、文字列の両側に 20 行しか表示されません。

注: - テキストがある行番号に一貫性がないため、これらから抜け出す
ことができないため、-A 20 を使用している理由です。
その後、検索を続行します。

要約: 4320101 を検索し、4320101 より上の 20 行 (または 1 行の空白) を出力し、4320101 より下の行をすべて出力します。

</eventUpdate>

調査を行っていると、これを行うために awk、nawk、または sed を有利に機能させる方法がわかりません。

4

6 に答える 6

1

sed/awk での後読みは常に注意が必要です。この自己完結型awkのスクリプトは、基本的に最後の 20 行を保存したままにします 。4320101保存されている行に到達すると、これらの保存された行を、空白行または不要な行が見つかるまで印刷し、停止します。 . その時点でprintallモードに切り替わり、eventUpdateに遭遇するまですべての行を出力し、それを出力して終了します。

awk '
function store( line ) {
    for( i=0; i <= 20; i++ ) {
        last[i-1] = last[i]; i++;
    };
    last[20]=line;
};
function purge() {
    for( i=20; i >= 0; i-- ) {
        if( length(last[i])==0 || last[i] ~ "Random" ) {
            stop=i;
            break
        };
    };
    for( i=(stop+1); i <= 20; i++ ) {
        print last[i];
    };

};
{
store($0);
if( /4320101/ ) {
    purge();
    printall=1;
    next;
};
if( printall == 1) {
    print;
    if( /eventUpdate/ ) {
        exit 0;
    };
};
}' test
于 2013-05-22T17:30:09.063 に答える
1

私があなたの要件を理解しているかどうか見てみましょう:

2 つの文字列があります。これを とKEYと呼びますLIMIT。そして、あなたは印刷したい:

  1. を含む行の前に最大 20 行までKEYですが、空白行がある場合は停止します。

  2. KEYを含む行と次の行を含む行の間のすべての行LIMIT。(これは、そのような行が 100 行を超えてはならないという要件を無視しています。それが重要な場合、追加するのは比較的簡単です。)

これを達成する最も簡単な方法(1)は、20 行の循環バッファーを保持し、 を押したときにそれを出力することですkey(2)sed または awk のどちらでも自明です。これは、2 アドレス形式を使用して範囲を出力できるためです。

それでは、awk で実行してみましょう。

#file: extract.awk

# Initialize the circular buffer
BEGIN          { count = 0; }
# When we hit an empty line, clear the circular buffer
length() == 0  { count = 0; next; }
# When we hit `key`, print and clear the circular buffer
index($0, KEY) { for (i = count < 20 ? 0 : count - 20; i < count; ++i)
                   print buf[i % 20];
                 hi = 0;
               }
# While we're between key and limit, print the line
index($0, KEY),index($0, LIMIT)
               { print; next; }
# Otherwise, save the line
               { buf[count++ % 20] = $0; }

これを機能させるには、 と の値を設定する必要がKEYありLIMITます。コマンドラインでそれを行うことができます:

awk -v "KEY=4320101" -v "LIMIT=</eventUpdate>" -f extract.awk $FILENAME

ノート:

  1. 正規表現の特殊文字をエスケープする必要がなく、正規表現が必要であるという要件のどこにもないためindex($0, foo)、より一般的な の代わりに使用しました。から始まるインデックスでinのインデックスを返すか、が見つからない場合。真偽値として使用され、真のものが見つかります。/foo/index(haystack, needle)needlehaystack10needleneedle

  2. next現在の行の処理を終了させます。この小さなプログラムが示すように、これは非常に便利です。

于 2013-05-22T21:10:41.890 に答える
1

これはうまくいくかもしれません(GNU sed):

sed ':a;s/\n/&/20;tb;$!{N;ba};:b;/4320102/!D;:c;n;/<\/eventUpdate>/!bc' file

編集:

  • :a;s/\n/&/20;tb;$!{N;ba};これにより、パターン スペース (PS) に 20 行のウィンドウが保持されます。
  • :b;/4320102!D;4320102これにより、パターンが見つかるまで上記のウィンドウがファイル内を移動します。
  • :c;n;/<\/eventUpdate>/!bc<\/eventUpdate>20 行のウィンドウが印刷され、パターンが見つかるまで後続の行が印刷され ます。
于 2013-05-22T21:27:17.313 に答える
0

最も簡単な方法は、ファイルの 2 つのパスを使用することです。最初に、ターゲット正規表現が見つかった範囲内の行番号を識別し、2 番目に、選択した範囲内の行を出力します。例:

awk '
NR==FNR {
    if ($0 ~ /\<4320101\>/ {
        for (i=NR-20;i<NR;i++)
            range[i]
        inRange = 1
    }
    if (inRange) {
        range[NR]
    }
    if ($0 ~ /<\/eventUpdate>/) {
        inRange = 0
    }
    next
}
FNR in range
' file file
于 2013-05-22T18:45:50.803 に答える