텍스트처리_04 입력 파일 해시값을 줄마다 추가해서 출력하기

 
  • 출처 : 유닉스 리눅스 쉘스크립트 예제사전_한빛미디어

명령어: paste, md5sum, read, awk
키워드: 해시, 페이스트, 컬럼
사용처: 파일에서 입력값을 읽어서 줄마다 해시값을 계산해서 CSV 파일에 출력하고 싶을 때


실행예제

$ cat data.txt
abcdefg
password
123456

$ ./paste.sh data.txt
abcdefg,7ac66c0f148de9519b8bd264312c4d64
password,5f4dcc3b5aa765d61d8327deb882cf99
123456,e10adc3949ba59abbe56e057f20f883e

스크립트

#!/bin/sh

# 해시값을 출력할 임시 파일을 초기화
tmpfile="hash.txt"  #-------------------------------------------- 1
: > $tmpfile  #-------------------------------------------------- 1

# 셸 구분자를 줄바꿈만 인식하도록 변경
IFS='
'  #------------------------------------------------------------- 2


# 지정한 텍스트 파일에서 한 줄씩 읽어들림
while read -r line  #-------------------------------------------- 3
do
    # MD5 해시 취득
    # 명령어에 파일명이 따라오므로 첫 번째 컬럼만 추출
    echo -n "$line" | md5sum | awk '{print $1}' >> $tmpfile  #--- 5
done < $1  #----------------------------------------------------- 4

# 원본 텍스트 파일과 해시를 출력한 임시 파일을 쉼표로 연결해서 표시
paste -d, "$1" $tmpfile  #--------------------------------------- 6

   

해설

이 스크립트는 지정한 텍스트 파일 각 줄마다 MD5 해시값을 계산해서 그 값을 컴마로 구분한 CSV 형식으로 출력합니다.

MD5는 해시 함수 중 하나로 입력된 값에서 128비트 해시값을 출력합니다. 해시값이란 메시지 다이제스트라고 부르며 입력값에 대해 해시 함수로 계산한 값입니다. 메시지가 깨졌는지 누군가가 몰래 고치지 않았는지 등을 간단히 확인할 수 있어 널리 사용됩니다.

  • 한 글자만 달라도 해시값은 크게 달라짐

      $ echo -n "ABCDEFGABCDEFGABCDEFGABCDEFGABCDEFG" | md5sum
      bd3b9bbc014d8f8ebc284d5d590bdb1a -
    
      $ echo -n "AACDEFGABCDEFGABCDEFGABCDEFGABCDEFG" | md5sum
      70709a03675c0677ca0a1ce9aea53f75 -
    

MD5 해시값을 얻어 md5sum 명령어로 출력한 예입니다. 여기서 “ABCDEFG”가 5번 반복된 문자열 해시값을 우선 구한 다음, 두 번째 B를 A로 바꾼 문자열 해시값을 구합니다. echo 명령어에서 줄바꿈을 하지 않는 -n 옵션을 써서 문자열로만 해시값을 구합니다. 한 글자만 바뀌었지만 출력된 해시값은 크게 다릅니다. 이렇듯 해시값을 비교하면 메시지가 변경되었는지 깨지지 않았는지 판정할 수 있습니다. 하지만 출력된 해시값에서 역으로 원래 입력값을 구하는 것은 어렵습니다.

예제 스크립트는 우선 입력 파일 각 줄을 읽어서 해시값을 계산하여 다른 파일에 출력합니다. 따라서 임시 파일의 초기화를 1에서 수행합니다.

2IFS에 줄바꿈을 대입해서 셸 구분자로 줄바꿈만 설정합니다. 셸은 스페이스 기호를 기본 구분자로 사용하므로 그냥 사용하면 스크립트에서 파일을 읽을 때 단어 앞머리 등에 스페이스가 있다면 구분자로 인식해서 제대로 된 해시값을 얻지 못합니다. 따라서 구분자를 미리 변경해둡니다.
IFS 설정방법은 아래 링크에서 설명했으니 참조하기 바랍니다.

IFS 참고: https://blessu1201.github.io/2024/02/05/linux-file-ls-case-048.html

3에서는 while문을 사용해 read 명령어로 지정한 입력 파일에서 한 줄씩을 셸 변수 line에 읽어들입니다. 4에서 while문 전체에 입력 리다이렉트하므로 명령행 인수로 지정한 파일에서 읽기 처리를 합니다. 그리고 3read 명령어에서 백슬래시()가 있는 문자를 그대로 다룰 수 있도록 -r 옵션을 사용합니다. -r 옵션이 없으면 “abcd\nefgh”라는 문자열에 있는 \n 을 줄바꿈 문자로 인식하게 됩니다. 5는 해시값을 계산한 처리 부분입니다. md5sum 명령어에 파이프로 연결한 echo 명령어를 사용해 값을 입력합니다. md5sum 명령어 출력에는 파일명이 따라오므로(예제에서는 표준 입력이므로) awk 명령어로 첫 번째 컬럼값만 추출합니다. 그 결과를 임시 파일 $tmpfile에 출력합니다. 

그 결과 임시 파일 $tmpfile 내용은 다음과 같은 해시값의 나열입니다.

7ac66c0f148de9519b8bd264312c4d64
5f4dcc3b5aa765d61d8327deb882cf99
e10adc3949ba59abbe56e057f20f883e

마지막으로 6에서 원래 파일 data.txt와 해시값을 기록한 임시 파일 $tmpfile을 연결합니다. paste 명령어를 사용하는데 두 텍스트 파일을 횡방향으로 연결하는 명령어입니다. 기본값은 탭 구분자를 사용하므로 여기에서는 CSV 파일이 되도록 쉼표를 -d 옵션으로 지정합니다(“-d,” 처럼 작성합니다). 이렇게 하면 원래 값과 해시값을 CSV 파일로 생성할 수 있습니다.

해시값을 다른 스크립에서 재이용하고 싶으면 일단 별도의 파일(hash.txt)에 출력해두면 됩니다.

   

주의사항

  • FreeBSD나 Mac에서는 md5sum이 아닌 md5 명령어를 사용합니다.
    echo $line | md5 | awk '{print $1}' >> $tmpfile
    
  • MD5는 해시값으로 128비트를 사용하므로 현재는 안전성이 높은 SHA 형식을 더 많이 사용합니다. 하지만 SHA 형식을 다루는 명령어는 OS에 따라 기본 설치되지 않을 수도 있으므로 여기에서는 MD5를 사용했습니다.