개요
관리하는 파일에 고유 식별 번호가 존재한다. 그런데 어떠한 이유로 인해 파일에 있는 고유 식별 번호가 중복되어있는 문제가 발생하였다.
관리 파일들은 management
디렉토리 및 그 하위 디렉토리에 .txt
확장자로 존재한다.
그러나 매번 중복되는 식별 번호를 수작업으로 찾는 것은 한계가 있다고 판단해 스크립트르 작성해보려고 한다.
요구사항 정의
해결하고자 하는 문제는 다음과 같다.
management
디렉토리에는 다양한 확장자의 파일이 존재함..txt
파일 외의 파일들은 검사 대상에서 제외.- 각
.txt
파일의 첫 줄에는 항상숫자;
형식의 고유 식별 번호가 붙어있음.- Ex.
00001;
- Ex.
- 하위 디렉토리(
management/dir
등)를 포함한 모든.txt
파일에서 식별 번호의 중복 여부를 확인해야 함. - 중복된 식별 번호가 발견되면 해당 파일의 경로를 출력
스크립트 설명
아래 코드들은 요구사항을 해결하기 위한 Bash 스크립트이다.
전체 코드를 먼저 살펴본 후, 한 줄씩 자세히 분석해보자.
#!/bin/bash
set -e
declare -A identifier_to_filepath
declare -A duplicate_identifiers
shopt -s globstar nullglob
for file in data/**/*.txt ; do
first_line="$(head -n 1 "$file")"
file_id="${first_line::-1}"
file_path="$(dirname "$file")/$(basename "$file")"
if [[ ${identifier_to_filepath[$file_id]} == "" ]]; then
identifier_to_filepath[$file_id]="$file_path"
continue
fi
if [[ ${duplicate_identifiers[$file_id]} == "" ]]; then
duplicate_identifiers[$file_id]="- ${identifier_to_filepath[$file_id]}"
fi
duplicate_identifiers["$file_id"]+=$'\n'
duplicate_identifiers["$file_id"]+="- $file_path"
done
shopt -u nullglob
if [[ ${#duplicate_identifiers[@]} == 0 ]]; then
echo "No Duplication found"
exit 0
fi
for id in "${!duplicate_identifiers[@]}"; do
echo
echo "ERROR: Duplicate id ${id} found"
echo "${duplicate_identifiers[$id]}"
done
exit 1
코드 분석
1. 스크립트 시작과 옵션 설정
#!/bin/bash
set -e
set -e
: 명령어 실행 중 하나라도 실패하면 스크립트가 즉시 종료되도록 설정한다. 중간에 오류가 발생했을 때 스크립트가 계속 실행되는 것을 방지할 수 있다.
2. 배열 선언
declare -A identifier_to_filepath
declare -A duplicate_identifiers
declare -A
명령어를 사용하여 두 개의 연관 배열을 선언한다. Bash에서는 기본 배열이 인덱스 기반이지만, -A 옵션을 사용하면 키-값 쌍으로 이루어진 연관 배열을 사용할 수 있다.id_to_file_path
: 각 파일의 식별 번호를 키로 하고, 해당 파일의 경로를 값으로 저장한다.duplicate_file_paths
: 중복된 식별 번호를 키로 하고, 해당 번호를 가진 파일들의 경로 목록을 값으로 저장한다.
3. 셸 옵션 설정
shopt -s globstar nullglob
shopt -s globstar
:**
를 사용하여 모든 하위 디렉터리를 재귀적으로 검색할 수 있도록 설정한다.shopt -s nullglob
: 글로빙 패턴이 일치하는 파일이 없을 경우, 패턴 자체를 그대로 문자열로 사용하지 않고 빈 목록으로 처리하도록 한다.
4. 파일 탐색과 식별 번호 추출
for file in management/**/*.txt ; do
first_line="$(head -n 1 "$file")"
file_id="${first_line::-1}"
file_path="$(dirname "$file")/$(basename "$file")"
for file in data/**/*.txt; do
: data 디렉터리와 하위 디렉터리의 모든 .txt 파일을 탐색한다.first_line="$(head -n 1 "$file")"
: 파일의 첫 번째 줄을 읽어 first_line 변수에 저장한다.file_id="${first_line::-1}"
: 첫 줄의 마지막 문자(;)를 제거하여 file_id에 저장한다.file_path="$(dirname "$file")/$(basename "$file")"
: 파일의 디렉터리와 파일 이름을 결합해 파일 경로를 저장한다.
5. 중복 검사 및 배열 업데이트
if [[ ${identifier_to_filepath[$file_id]} == "" ]]; then
id_to_file_path[$file_id]="$file_path"
continue
fi
if [[ ${duplicate_identifiers[$file_id]} == "" ]]; then
duplicate_file_paths[$file_id]="- ${identifier_to_filepath[$file_id]}"
fi
duplicate_identifiers["$file_id"]+=$'\n'
duplicate_identifiers["$file_id"]+="- $file_path"
if [[ ${identifier_to_filepath[$file_id]} == "" ]]; then
: 식별 번호가 아직identifier_to_filepath
배열에 없으면 추가하고 다음 파일로 넘어간다.- 중복이 발견된 경우, 해당 식별 번호를
duplicate_identifiers
배열에 저장한다. 처음 발견된 파일 경로를 목록에 추가하고, 이후 발견된 파일 경로들도 추가한다.
6. 옵션 해제
shopt -u nullglob
nullglob
옵션을 해제하여 이후 스크립트의 동작에 영향을 주지 않도록 한다.
7. 중복 검사 결과 출력
if [[ ${#duplicate_identifiers[@]} == 0 ]]; then
echo "No Duplication found"
exit 0
fi
for id in "${!duplicate_identifiers[@]}"; do
echo
echo "ERROR: Duplicate id ${id} found"
echo "${duplicate_identifiers[$id]}"
done
exit 1
if [[ ${#duplicate_identifiers[@]} == 0 ]]; then
:duplicate_identifiers
배열에 요소가 없으면, 중복이 없다는 메시지를 출력하고 스크립트를 정상 종료한다.- 중복된 식별 번호가 있으면 해당 번호와 중복된 파일 경로를 출력하고, 스크립트를 오류 코드 1로 종료한다.
실행 예제
management
디렉토리에 다음과 같은 파일 구조가 있다고 가정해보자.
data/
├── a.txt # 첫 줄: 00001;
├── b.txt # 첫 줄: 00002;
├── dir/
│ ├── c.txt # 첫 줄: 00001;
│ └── d.txt # 첫 줄: 00003;
스크립트를 실행하면 a.txt와 c.txt의 식별 번호가 중복되었다는 오류 메시지가 출력될 것이다.
실행 결과
ERROR: Duplicate id 00001 found
- data/a.txt
- data/dir/c.txt