awk は for ループでファイルの代わりにファイル名を使用します。

awk は for ループでファイルの代わりにファイル名を使用します。

さて、私はawkを使っていくつかのファイルから特定の列を抽出し、それを配列に入れてソートする必要があります。次に、抽出されたソート列でいくつかの値を見つけるにはawkを使用する必要がありますが、今私のforループにはいくつかの問題があります。

for var in $1 $2
do
myarr=($(awk -v row=$3 -F';' '$row!="" {print $row}' $var))
sorted_array=( $( printf "%s\n" "${myarr[@]}" | sort -n ) )
echo "${sorted_array[@]} $var"
done

出力は次のとおりです

 dbdump.csv
 dbdump2.csv

これは、列を抽出したい2つのcsvファイルの名前です。コンテンツを検索するにはこのスクリプトが必要です。また、より高速なアルゴリズムの使用方法を提案できる場合は、そうします。これは私がいくつかのbashスクリプトを学び、いくつかのコードを書くことです。

入力ファイルには次のレコードが含まれており、そのうちの2つは列3に一致する値を持っていません(私の管理者が言ったことです)。

1101590479;Frank Haemers;;20060310;1;RESI;;01;06;0007;0000000000;;CRM000;
1101590473;Van KetsmJan;;20060310;2;PROF;;01;08;;0000000000;75;CRM000;0686143950

これら2つのファイルには約500万のレコードが含まれています。特定の数のパターンを含む別のファイルがあり、これら2つの巨大なcsvファイルを見つける必要があり、パターンの1つが2つのファイルで一致する場合は別のファイルに出力する必要があります。たとえば、次のようになります。

echo "$pattern has been found in $file"

パターンテキストファイル内のすべてのパターンに対してこれを行う必要があります。

答え1

シェルスクリプトを作成するときは、検証済みの変数を最初に指定し、最後にファイル名を指定することをお勧めします。これにより、指定されたファイル数を変更できます。あなたの場合は、列番号、パターンを含むファイル、および処理する2つ(またはそれ以上)のファイル名があります。ここで Bash スクリプトを起動してください

#!/bin/bash
if [ $# -lt 2 ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
    echo ""
    echo "Usage: $0 [ -h | --help ]"
    echo "       $0 COLUMN PATTERNFILE [ FILE(s) ... ]"
    echo ""
    exit 0
fi

上記のセクションでは、if古いPOSIXシェル形式を使用し、ほとんどdashの古いシェルだけでなく他のPOSIXシェルでも機能しますsh。目的は、ユーザーがコマンドライン引数を指定しない場合、-hまたは指定した場合のみ、スクリプトが--help短いヘルプテキストのみを印刷することです。

ところで、ヘルプテキストを拡張する必要があります。作成した内容を忘れてから2〜3ヶ月後にヘルプテキストの目的を理解する方が簡単です。 (このようなことはいつも私に起こり、私はそのようなことを経験しました。場所このようなスクリプトを使用するので、このアプローチは少し努力する価値があると思います。 )

次に、必要な引数(上記の1つのみ)を抽出して、コマンドラインで指定されたすべてのファイル名を参照するためにshift使用できるように取り出します。"$@"

column=$1
patternfile="$2"
shift 2

明示的に必要でない場合でも、シェルで拡張したい項目の周りに二重引用符を入れるのが好きです。私がシェルスクリプトで経験している実際の問題の多くは、次の理由からです。忘れる必要に応じて参照拡張を参照してください。この方法は覚えやすく、「二重引用符は実際には必要ありません」という迷惑な鼻を除いて、何の害もありません。

awkそれからそれを使って入力ファイルを処理しましょう。

awk -v column=$column \
  'BEGIN {
       RS = "[\t\v\f ]*(\r\n|\n\r|\r|\n|)[\t\v\f ]*"
       FS = "[\t\v\f ]*;[\t\v\f ]*"
   }

上記の最初の行の終わりにあるバックスラッシュは、単にコマンドが次の行に続くことをシェルに伝えます。また、終了する一重引用符がないため、'下の行は実際に私たちが提供したコマンドライン文字列引数の連続ですawk

awkのルールは、BEGINファイルが処理される前に実行されます。上記はRSレコード区切り文字を改行規則として設定し、各行の先頭または末尾のスペースを含みます。同様に、フィールド区切り文字はセミコロンですが、その周囲にスペースが含まれます。したがって、空白のない最初のフィールドと2番目のフィールドにはa ; b2つのフィールドがあります。ab

どの入力ファイルが処理されているかを追跡するには、次のイディオムを使用します。

    FNR==1 { ++filenum }

単に私たちが処理する各入力ファイルの最初のレコードに対して変数を増やすという意味であればfilenum。初期化されていない変数を増やすことは、ゼロを増やすのと同じであるため、1最初の入力ファイルを取得する式です。

最初の入力ファイル(パターンファイル)の各行の内容を覚えておきたいです。

    filenum==1 { pattern[$0] }

awk 配列は連想配列なので、既知のパターンを維持するために連想配列を使用できます。上記では、興味深いawk機能を利点として使用しました。まだ存在しない連想配列エントリにアクセスしようとすると、awkはそのエントリを生成します!

残りのファイルについては、フィールド$column(awk変数のawkスクリプトレットに提供されているcolumn)が最初のファイルに表示されているパターンと(正確に)一致することを確認し、そうであればレコード全体を印刷します。

    filenum > 1 && ($column in pattern) { printf "%s\n", $0 }

$column上記はシェルスクリプトとは異なる意味を持ちます。これにはcolumn変数があり、$column現在のレコードの '番目のフィールド値に展開されます(ただし、ゼロ列はレコード全体です)。column構文は、キーが含まれていることをfoo in array確認するために使用されるawkismです。したがって、要約すると、2番目と追加の入力ファイルの場合、最初のフィールド値が最初の入力ファイルにリストされている場合は、そのレコードが印刷されます。標準出力として。arrayfoocolumn

まだコマンドライン引数文字列にあるので、単一引用awk符文字列を閉じる必要があります。また、ファイル名を指定したいと思います。

    ' "$patternfile" "$@"

このawkスクリプトが完了しました。

答え2

パターンリストとファイルセットを取得し、特定の列の各パターンに一致するすべてのファイル名を印刷するには、GNU awk(Linuxのデフォルト)のみを使用できます。

awk -F';' '{
                if(NR==FNR){ 
                    p[$0]++; 
                    next
                } 
                if($3 in p){
                    printf "%s found in %s\n", $3,FILENAME; 
                    nextfile
                }
            }' patterns file1.csv file2.csv fileN.csv

説明する

  • awk -F';':フィールド区切り記号をに設定します;
  • if(NR==FNR){ p[$0]++;next}NR現在の入力行番号とFNR現在のファイルの行番号。どちらも最初のファイルを処理するときにのみ同じです。したがって、パターンファイル(最初のファイル)の各行を配列として保存し、そのpnextに移動します。パターンファイルに対してのみ実行されます。
  • if($3 in p){printf "%s found in %s\n", $3,FILENAME; nextfile:今csvファイルを見ています。 3番目のフィールドが配列の要素の1つである場合p(スキーマファイルにある場合)、3番目のフィールド(スキーマ)とそのフィールドを含むファイル名を印刷します。次に、次のファイルに移動します。このFILENAME変数は、現在処理中のファイルのパスを保持します。これはnextfile言うように正確に実行するgawk関数です。処理する次のファイルにジャンプします。

たとえば、次のファイルが提供されます。

$ cat patterns 
foo
bar
baz

$ cat file1.csv 
blah;blah;foo;blah
blah;blah;foo;blah
blah;blah;foo;blah

$ cat file2.csv 
blah;blah;bar;blah

$ cat file3.csv 
blah;blah;baz;blah

以下の結果が出力されます。

$ awk -F';' '{if(NR==FNR){p[$0]++; next} if($3 in p){printf "%s found in %s\n", $3,FILENAME; nextfile}}' patterns file*csv 
foo found in file1.csv
bar found in file2.csv
baz found in file3.csv

各パターンが複数のファイルに存在できる場合は、少し異なるアプローチを使用できます。

awk -F';' '{
            if(NR==FNR){ 
                p[$0]++; 
                next
            } 
            if($3 in p && !seen[FILENAME][$3]){
                printf "%s found in %s\n", $3,FILENAME; 
                seen[FILENAME][$3]++
            }
        }' patterns file1.csv file2.csv fileN.csv

今回はありません。nextfileファイル全体を処理し、特定のファイルでパターンが見つかるたびにカウンタを増やす必要があるため、同じパターンを複数回報告しません。

したがって、file1.csv上記の内容を次のように変更してください。

$ cat file1.csv 
blah;blah;foo;blah
blah;blah;baz;blah
blah;blah;bar;blah
blah;blah;foo;blah

私達は次を得ました:

$ awk -F';' '{if(NR==FNR){p[$0]++; next} if($3 in p && !seen[FILENAME][$3]){printf "%s found in %s\n", $3,FILENAME; seen[FILENAME][$3]++}}' patterns file*csv 
foo found in file1.csv
baz found in file1.csv
bar found in file1.csv
bar found in file2.csv
baz found in file3.csv

速度が遅すぎる(大容量ファイルの場合)、ファイル内のすべてのパターンが見つかった場合は、ファイルの読み取りを停止するように変更できます。

awk -F';' '{
            if(NR==FNR){ 
                p[$0]++; 
                next
            } 
            if($3 in p && !seen[FILENAME][$3]){
                printf "%s found in %s\n", $3,FILENAME; 
                seen[FILENAME][$3]++
            }
            if( length(seen[FILENAME]) == length(p) ){
                nextfile
            }
           }' patterns file1.csv file2.csv fileN.csv

関連情報