다양한 사용자 데이터가 포함된 두 개의 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
먼저 두 번째 파일에서 계정이 잠긴 사용자의 사용자 이름을 읽은 다음 첫 번째 파일에서 해당 사용자의 이메일 주소를 추출합니다(그런 다음 이메일을 읽기 위해 로그인할 필요가 없기를 바랍니다).
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
위의 코드는 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 규칙을 사용하는 경우 이 작업을 수행해야 합니다. ;
위의 파이프라인은
- 사용자 이름을 기준으로 두 파일을 연결합니다(공백이 중요함).
- 잠긴 사용자의 데이터를 추출합니다(이 시점에서 유언장은 파이프라인에서 이 시점
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