Bash: 두 csv 파일의 데이터 연결

Bash: 두 csv 파일의 데이터 연결

다양한 사용자 데이터가 포함된 두 개의 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.csvNF$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 규칙을 사용하는 경우 이 작업을 수행해야 합니다. ;위의 파이프라인은

  1. 사용자 이름을 기준으로 두 파일을 연결합니다(공백이 중요함).
  2. 잠긴 사용자의 데이터를 추출합니다(이 시점에서 유언장은 파이프라인에서 이 시점 0으로 변경되었습니다 ).False
  3. 이메일 주소를 추출합니다.
  4. 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

관련 정보