多くのテキストファイルをフィルタリングしたいです。各ファイルには長い数字のリストが含まれています。ファイルは、各数字の連続した数字に基づいてフィルタリングする必要があります。
リストの例では、次のファイルのいずれかを構成します。
入力ファイルdata.log
:
12365
91738
349874
128152639
1234
7654
08767
1234567
私の考えでは:
1 - 2つの連続した数字を含む数字でこのリストをフィルタリングします。予想される出力は次のとおりです。
12365
349874
128152639
1234
7654
08767
1234567
2〜3つの連続した数字を含む数字でこのリストをフィルタリングします。予想される出力は次のとおりです。
12365
349874
1234
7654
08767
1234567
3〜4つの連続した数字を含む数字でこのリストをフィルタリングします。予想される出力は次のとおりです。
1234
7654
1234567
4〜5つの連続した数字を含む数字でこのリストをフィルタリングすると、予想される出力は次のようになります。
1234567
数字内の連続した数字の順序が最小のものから最大のもの(例:1234...など)、または大きいものから小さいもの(例:54321)の場合は、出力に含める必要があります。
答え1
grep
、tee
およびを使用して、-ismsでいっぱいのトリッキーな小さな関数を作成しrev
ます。bash
dqs() { a=${2:-123456789} ; [ "$1" -ge 2 ] &&
grep -iF "$(eval eval printf '%s\\\\n' \\$\\{a:\{0..$((${#a}-$1))\}:$1\\} |
tee >(rev) )"
}
テストを受けてください:
dqs 5 < data.log
1234567
dqs 4 < data.log
1234
7654
1234567
dqs 3 < data.log
12365
349874
1234
7654
08767
1234567
仕組み:
printf
目的の長さのシーケンスのリストを印刷します(例:123、234、など。)、tee
ミラーを取り付けます(つまり右から左へ、または後ろに)rev
次に、grep -f <(...)
そのリストの項目の標準入力を検索します。
このシーケンスのリストを作成するには通常、ループまたはseq
両方が必要ですが、ここでは次のようにします。bash
配列表現, と結合部分文字列拡張、少し残った山水。ただし、bash
インタプリタが必要な順序でこれらの操作を実行できないため、これは不可能です。だから私たちは正しい順序で仕事をするためにeval eval
いくつかの戦略を使います。\\\
bash
ここでは[ "$@" -gt 0 ] &&
機能的には必要ありませんが、持っている方が安全です。それはそこにdqs
あることを保証します1つだけ数値引数です。それ以外の場合はgrep
実行されません。これはeval eval
何もしません。邪悪な。
ボーナス:2番目の引数を追加すると、他のシーケンスが123456789
変更されてもコードは機能し続けます。たとえば、dqs 4 123456789ABCDEF
4桁の16進数シーケンス(および逆順)が検索され、dqs 3 $(printf %s {a..z})
3桁のアルファベットシーケンスが検索されます。
# search `man bash` for the three most popular words
# that have 3 three char alphabetic runs
man bash | tr ' ' '\n' | sort | uniq -c | sort -gr |
dqs 3 $(printf '%s' {a..z}) | head -3
出力:
92 first
76 default
38 environment
答え2
非常に大きなファイルが多いと、awkの正規表現の一致が遅くなる可能性があります。 1つのアプローチは、grepを使用して難しい作業を実行し、awkを使用して検索する文字列のリストを作成することです(これをハードコーディングしたくないからです)。つまり
$grep -E '12|98|23|87|34|76|45|65|56|54|67|43|78|32|89|21' data.log
2人のキャラクターに対してこれを行うことは可能ですが、最大9人のキャラクターに対して行うことができるようにしたいです。複数のパターン検索をサポートするには、grepを拡張するには-Eが必要です(12 | 98は2つのパターンです)。通常のol 'grepはこれを許可しません。
awkは文字列123456789を繰り返して連続セグメントを取り出すことができますが、前後に移動しようとしています。
$awk 'BEGIN {f=123456789 ; b=987654321 ; for(i=1;i<9;i++) print substr(f,i,2),substr(b,i,2)}'
12 98
23 87
34 76
45 65
56 54
67 43
78 32
89 21
長さが2にハードコードされないように何かを追加しましょう(-vn = 3はawkスクリプト内で変数n = 3を設定します)。
$awk -vn=3 'BEGIN {f=123456789 ; b=987654321 ; for(i=1;i<11-n;i++) print substr(f,i,n),substr(b,i,n)}'
123 987
234 876
345 765
456 654
567 543
678 432
789 321
そして(ほとんど!)出力レコード区切り記号(ORS)と出力フィールド区切り記号(OFS)
$awk -vn=3 'BEGIN {ORS="|" ; OFS="|" ; f=123456789 ; b=987654321 ; for(i=1;i<11-n;i++) print substr(f,i,n),substr(b,i,n)}'
123|987|234|876|345|765|456|654|567|543|678|432|789|321|
321以降の最後のパイプを削除する必要があります。それ以外の場合はgrepがすべてのエントリと一致するため、sed '.$//'を追加して、文字列の終わり($)の前の最後の文字を何もないものに置き換えます。
$awk -vn=3 'BEGIN {ORS="|" ; OFS="|" ; f=123456789 ; b=987654321 ; for(i=1;i<11-n;i++) print substr(f,i,n),substr(b,i,n)}' | sed 's/.$//'
これでシェルスクリプトに入れて、一般的な検索を行うことができます。
$cat t.sh
#!/bin/bash
grep -E `awk --assign n=$1 'BEGIN {OFS="|" ; ORS="|" ; f=123456789 ; b=987654321 ; for(i=1;i<11-n;i++) print substr(f,i,n),substr(b,i,n)}' | sed 's/.$//'` $2
$chmod 775 t.sh
$./t.sh 4 data.log
1234
7654
1234567
答え3
多くの大容量ファイルは、これをすばやく実行する必要があることを示しています。これは次のことを意味しますwhile read
ルーティングはできません。。ここで気づくべきことの1つは、各運動が(少なくとも)小さなパターンセットの1つと一致するように縮小できることです。本物またはなどのクイック使用grep
または同様のツール。たとえば、5桁のシーケンスの場合:rg
ack
grep -e 12345 -e 23456 […] -e 65432 -e 54321
man grep
詳細と使い方はこちらグレッグのウィキBashをすばやく学びましょう。