複数のセクションで構成されるテキストファイルがあり、各セクションには2つのヘッダー行とスペースで区切られた単語で構成される本文行があります。例は次のとおりです。
Shares for DED-SHD-ED-1:
[--- Listable Shares ---]
backup backup2
Shares for DED-SHD-ED-2:
[--- Listable Shares ---]
ConsoleSetup REMINST SCCMContentLib$ SCCMContentLibC$ SEFPKGC$ SEFPKGD$ SEFPKGE$ SEFSIG$ Source UpdateServicesPackages WsusContent backup backup2
Shares for DED-SHD-BE-03:
[--- Listable Shares ---]
backup backup2 print$
削除したいボディラインで見るとすべての単語が発生3回以上。
- 削除したいみんな「最初の2回以降のすべての発生」と同様に発生。
- 一致させるトークンは、スペースで区切られた「単語」です。つまり、
print$
英数字部分だけでなく全体ですprint
。 - 一致は「完全な単語」にのみ適用する必要があります。つまり、部分文字列の一致は適用されません。たとえば、すべての発生は
backup
削除にのみ含まれ、backup
削除には含まれませんbackup2
。 - ヘッダー行(
Shares for ...
および)は考慮されません。[--- Listable Shares ---]
上記の入力に必要な出力は次のとおりです。
Shares for DED-SHD-ED-1:
[--- Listable Shares ---]
Shares for DED-SHD-ED-2:
[--- Listable Shares ---]
ConsoleSetup REMINST SCCMContentLib$ SCCMContentLibC$ SEFPKGC$ SEFPKGD$ SEFPKGE$ SEFSIG$ Source UpdateServicesPackages WsusContent
Shares for DED-SHD-BE-03:
[--- Listable Shares ---]
print$
ご覧のように、3つの部分の本文行にbackup
andという単語だけがbackup2
表示されるため、削除されました。ただし、ヘッダー行は編集対象とは見なされないため、セクションヘッダー行の、およびShares
はfor
変更されていません。Listable
いくつかの注意:
- これらのファイルのサイズは100kBから1MBまでです。
- 同様の解決策が見つかり
awk '++A[$0] < 3'
ましたが、最初の2つの項目を維持し、行全体を見てみましょう。 - 私は特にAwkベースのソリューションを探しているのではなく、何でも(Perlを除く;)することができます。
ベストアンサー1
最大1MBのファイルを処理する必要があるため、効率を向上させるには複数の配列の反転が必要です。単語を削除しているので、正確な間隔を維持することは重要ではないと思うので、代替行の各単語の前にTABが続きます。
これは、独自の awk プログラムを含む単一のシェル関数を含む Bash スクリプトです。入力ファイル引数を使用してstdoutに出力します。
結果をどのように確認したいのか分かりません。私は開発中に多くのデバッグをしました。たとえば、削除された単語とその頻度をstderrに書き込むのは簡単です。
#! /bin/bash
delByFreq () {
local Awk='
BEGIN { SEP = "|"; Freq = 3; }
#.. Store every input line.
{ Line[NR] = $0; }
#.. Do not look for words on header lines.
/^Shares for / { next; }
/--- Listable Shares ---/ { next; }
#.. Keep an index to row/column of every unique word.
#.. So like: Ref ["backup2"] = "|2|3|5|1|5|7";
function Refer (row, txt, Local, f) {
for (f = 1; f <= NF; ++f)
Ref[$(f)] = Ref[$(f)] SEP row SEP f;
}
{ Refer( NR, $0); }
#.. Rearrange field indexes by line.
#.. So like: Del[row] = "|3|7|11"; for field numbers.
function refByLine (Local, word, j, n, V) {
for (word in Ref) {
n = split (Ref[word], V, SEP);
if (n <= 2 * Freq) continue;
for (j = 2; j < n; j += 2)
Del[V[j]] = Del[V[j]] SEP (V[j+1]);
}
}
#.. For every line with deletions, cross off the frequent words.
function Deletions (Local, row, j, f, n, V, X) {
for (row in Del) {
split (Del[row], V, SEP);
split ("", X, FS); for (j = 2; j in V; ++j) X[V[j]];
#.. Rebuild the line in field order.
split (Line[row], V, FS); Line[row] = "";
for (j = 1; j in V; ++j)
if (! (j in X)) Line[row] = Line[row] "\t" V[j];
}
}
function Output (Local, r) {
for (r = 1; r in Line; ++r) printf ("%s\n", Line[r]);
}
END { refByLine( ); Deletions( ); Output( ); }
'
awk -f <( printf '%s' "${Awk}" ) "${1}"
}
delByFreq "${1}"