web-dev-qa-db-ja.com

Bash:2つのcsvファイルのデータを結合する

さまざまなユーザーデータを含む2つのcsvファイルがあります。それらは1つの共通フィールド(ユーザー名)を共有します。

file A:
username ; Fullname ; mail
Bob      ; Bob Hope ; [email protected]

file B:
username ; LastLogonTime  ; AccountStatus (locked=0 or unlocked=1)
Bob      ; 2018-10-01 etc.; 0

監査の目的で、Bashを使用してAをループし、アカウントがロックされているかどうかをBとクロスチェックします。この場合、ユーザーをAのメールアドレスに送信できます。

awk -F";"

aを超えることができます。簡単ですが、Bでクロスチェックループを実行しようとすると途方に暮れます。

3
DavDav

awkを使用して、最初に2番目のファイルからアカウントがロックされているユーザーのユーザー名を読み取り、次に最初のファイルからこれらのメールアドレスを抽出します(メールを読むためにログインする必要がないことを期待します) ):

awk -F ';' 'NR == FNR && $NF == 0    { names[$1] }
            NR != FNR && $1 in names { print $NF }' B.csv A.csv

これは、各ユーザー名の両方のファイルで、ユーザー名の前後に同じ量の空白があることを前提としています。そうでない場合は、-F ' *; *'を使用して、awkが使用している区切り文字にスペース文字を含めることができます。また、データに;文字が埋め込まれていないことも前提としています。

NRは、現在のレコード全体のレコード(行)番号であり、FNRは同じ番号ですが、現在のファイル内にあります。 NR == FNRの場合、コマンドラインで指定された最初のファイル(B.csv)から読み取っています。 NFは現在のレコードのフィールド(列)の数であり、$NFは最後のフィールドのデータです(および$1は最初のフィールドのデータです)。

上記のコードは、最初のファイル(B.csv)から読み取られた、ロックアウトされたユーザーのユーザー名をキーとする連想配列/ハッシュnamesを使用しています。 $1 in namesがその配列のキーである場合、$1はtrueになります。

これをループに入れます:

awk -F ';' 'NR == FNR && $NF == 0    { names[$1] }
            NR != FNR && $1 in names { print $NF }' B.csv A.csv |
while read addr; do
    printf 'Would send an email to "%s"\n' "$addr"
    #mail -s 'Account locked' "$addr" <template-email.txt
done

またはそれらの線に沿って何か。ループ内でこの方法で電子メールアドレスを読み取ると、周囲の空白が削除されます。上記のループは電子メールを送信しませんが、送信する必要があるアドレスを出力します。 mailの前に#を削除して(およびフォームメールをtemplate-email.txtに書き込んで)、実際にメールを送信します(ただし、別の方法で送信することもできます)。


csvkit を使用:

csvjoin -d ';' -c 1 A.csv B.csv |
csvgrep -c 5 -m False |
csvcut -S -c 3 | sed 1d

CSVkitは、CSVファイルを操作するためのCSV解析ツールを提供します。これは、CSVデータが「単純」でない場合、つまり、埋め込まれた;文字を引用するためにCSVルールを使用する場合などに必要になります。上記のパイプラインは

  1. ユーザー名で2つのファイルを結合します(空白は重要です)。
  2. ロックアウトされているユーザーのデータを抽出します(パイプラインのこの時点で0Falseに変更されます)。
  3. メールアドレスを抽出します。
  4. CSVヘッダーを削除します(最後のsedコマンドを使用)。
5
Kusalananda