- 출처 : 유닉스 리눅스 쉘스크립트 예제사전_한빛미디어
명령어: awk
키워드: 평균값, CSV 파일
사용처: CSV 파일에서 특정 컬럼값의 평균값을 계산해서 파일로 출력하고 싶을 때
실행예제
$ cat data.csv <---------- 자료 파일 확인
0001,Kim,45
0002,Lee,312
0003,Park,102
0004,Kang,3
$ ./csv-avg.sh data.csv
$ cat data.avg <---------- 평균값이 기록된 [파일명.avg]이 생성됨
115.5
스크립트
#!/bin/sh
# CSV 파일이 존재하지 않으면 종료
if [ ! -f "$1" ]; then #-------------------------------------------- 1(if문)
echo "대상 CSV 파일이 존재하지 않습니다: $1" >&2
exit 1
fi
# 확장자를 제외한 파일명 취득
filename=${1%.*} #-------------------------------------------------- 2
awk -F, '{sum += $3} END{print sum /NR}' "$1" > ${filename}.avg #--- 3
해설
이 스크립트는 명령행 인수를 지정한 CSV 파일의 세 번째 컬럼에서 값의 평균값을 계산해서 출력합니다. 평균값은 원본 파일명에 확장자 .avg를 붙인 파일에 출력합니다. 여러 CSV 파일마다 평균값을 출력하고 싶을 때 사용하면 됩니다.
여기에서 사용하는 CSV 파일은 “ID번호, 이름, 점수” 형식이라고 가정합니다.
셸 스크립트에서 수치 계산은 expr 명령어를 자주 사용합니다. 하지만 expr은 정수 계산만 가능하므로 소수가 포함된 계산에서는 사용할 수 없습니다. 셸 스크립트에서 소수를 포함한 계산을 할 때는 bc 명령어를 사용하기도 하는데 여기에서는 간단히 awk 명령어를 사용해봅니다.
1
에서 대상 CSV 파일 존재를 확인합니다. -f 옵션은 대상이 일반 파일인지 확인하는데 부정 연산자 !를 함께 서서 대상이 일반 파일이 아니면 에러를 표시하고 종료합니다.
2
에서 파일 확장자를 제외한 파일명을 취득합니다. 여기서 셸 파라미터 확장을 사용한 문장열 치환으로 셸 변수 $1에서 ‘.(점)과 그 이후에 이어진 임의 문자열’을 제거합니다. $1에는 명령행 인수로 CSV 파일이 넘어오므로 확장자 .csv를 제거할 수 있게 됩니다.
3
은 평균값을 계산하는 awk 명령어입니다. 우선 awk 명령어에서 구분자로 ,(쉼표)를 지정하기 위해 -F 구분자 지정 옵션에 ,를 설정합니다. awk 명령어 표현은 {}는 각 줄마다 실행하고 END {}는 마지막 줄을 읽은 다음 실행하게 됩니다. 우선 {sum += $3}으로 각 줄의 $3(세 번째 컬럼인 점수)을 변수 sum과 더합니다.
END에서 사용한 NR은 awk 내장 변수로 현재 처리한 줄 번호가 들어 있습니다. 즉 END{print sum / NR}이 실행되는 것은 마지막 줄이므로 NR에는 파일 줄 수가 들어갑니다. NR값으로 sum을 나누면 점수 평균값을 구할 수 있습니다.
점수 평균값은 앞서 확장자를 제외한 파일명에 확장자 .avg를 붙인 파일에 리다이렉트해서 출력합니다.
주의사항
-
awk에서 구분자를 쉼표로 설정할 때 -F,가 아니라 -F ‘,’ 라고 지정해도 됩니다. 어느 쪽을 사용해도 괜찮지만 다른 사람의 코드를 읽을 때 두 가지 지정 방법이 있다는걸 기억하기 바랍니다.
-
이 스크립트는 값 자체에 쉼표가 포함된 CSV 파일에 대응하지 않습니다.
-
예제에서는 CSV 파일 내용을 확인하지 않으므로 만약 빈 파일은 0으로 나누게 되어서 에러가 발생합니다.