さまざまなユーザーデータを含む2つのcsvファイルがあり、共通フィールド(ユーザー名)を共有します。
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に対してクロスチェックループを実行しようとするとパニックになります。
答え1
を使用して、awk
最初に2番目のファイルからアカウントがロックされているユーザーのユーザー名を読み、次に最初のファイルからそのユーザーのEメールアドレスを抽出します(その後、Eメールを読むためにログインする必要はありません)。
awk -F ';' 'NR == FNR && $NF == 0 { names[$1] }
NR != FNR && $1 in names { print $NF }' B.csv A.csv
これは、2つのファイルの各ユーザー名の周りに同じ数のスペースがあると仮定します。そうでない場合は、使用する-F ' *; *'
区切り文字にスペース文字を含めることができますawk
。また、;
データに含まれる文字がないとします。
NR
は現在のレコード全体のレコード(行)番号で、FNR
現在のファイル内で同じですが同じ番号です。その場合は、コマンドライン()で提供されている最初のファイルから読み込みます NR == FNR
。は現在のレコードのフィールド(列)の数、は最後のフィールドのデータです(そして最初のフィールドのデータです)。B.csv
NF
$NF
$1
上記のコードは、names
最初のファイル()から読み取られたロックされたユーザーのユーザー名としてキー付きの連想配列/ハッシュを使用しますB.csv
。これがこの配列のキーであれば$1 in names
真です。$1
ループに入れます。
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ルールを使用する場合は、これを行う必要があります。;
上記のパイプラインは
- ユーザー名に基づいて2つのファイルを関連付けます(スペースが重要です)。
- ロックされたユーザーのデータを抽出します(この時点で、遺言はパイプラインからこの時点に
0
変更されました)。False
- メールアドレスを抽出します。
- CSVヘッダーを削除します(最後の
sed
コマンドを使用)。
答え2
次のタスクを実行するには、特別なツールを使用します(データベースとも呼ばれます)。
# Remove spaces around the field separator
sed -i.fixed 's/ *\; */\;/g' a
sed -i.fixed 's/ *\; */\;/g' b
# Add to sqlite database
echo -e '.separator ";"\n.import a.fixed a' | sqlite3 db.sqlite
echo -e '.separator ";"\n.import b.fixed b' | sqlite3 db.sqlite
# Select whatever you need
echo -e 'select a.username,a.mail,b."AccountStatus (locked=0 or unlocked=1)" from a join b on a.username = b.username;' | sqlite3 db.sqlite
awk
解決策:
users=( $(awk -F";" 'NR>1{print $1";"$3}' a) )
for u in "${users[@]}"; do
username=$(echo "$u" | cut -d';' -f1)
mail=$(echo "$u" | cut -d';' -f2)
awk -v "u=$username" -v "m=$mail" -F';' 'NR>1 { if ($3 == 0) print "User "u" ("m") is locked"; }' b
done
答え3
#!/bin/bash
cat fileA.txt | sed 1d | while IFS=';' read -r line; do #read fileA.txt starting with line #2
name=$(echo $line | awk '{print $1}') #find names in each line/column 1 of the table
lock_status=$(grep $name fileB.txt | awk '{print $5}') # find lock/unlock status in fileB.txt
if [[ "$lock_status" -eq 0 ]];then
echo "Locked: To mail the user : replace echo by the command mail";
else
echo "unlocked";
fi
done
答え4
まず、区切り文字の周りにスペースがある場合は、@RoVoが言ったようにスクリプトからスペースを削除する必要があります。 sedコマンドはこれを行います。
第二に、デフォルトでは固定fileAの各行を読み、ユーザー名と電子メールアドレス、オプションでユーザーのフルネームを取得するwhileループが必要です。その後、固定ファイルBでこのユーザーの状態を確認しようとします。
次の小さなループを開始できます。
#!/bin/bash
# Remove spaces around delimiter
sed -i.fixed 's/[ ]*\;[ ]*/\;/g' fileA
sed -i.fixed 's/[ ]*\;[ ]*/\;/g' fileB
# Read in each line from the fixed fileA
while read l; do
# Skip the header line
[[ ${l} =~ ^username ]] && continue
# Get the user from the line that was read in.
u=$(echo ${l} | awk -F\; '{print $1}')
# Get the lock status for that user from the fixed fileB
l=$(awk -F\; -v u=${u} '{if ($1 == u) {print $3}}' fileB.fixed)
# Echo out the 2 fields.
echo ${u}=${l}
# Other stuff can go here.
done <fileA.fixed
exit 0