6 쉘 스크립트
매일 반복되는 작업에 지쳤느라? 어제도 오늘도 비슷비슷한 명령어들을 반복 입력하면서 오타가 날까 봐 걸리고 있는가? 이제 쉘을 진짜 강력한 도구로 만들어보자.
그 마법의 열쇠는 바로 쉘 스크립트다. 자주 사용하는 명령어들을 하나의 파일에 모아두면, 복잡한 작업도 단 한 번의 명령으로 그냠처럼 다시 실행할 수 있다. 이렇게 저장된 명령어 묶음을 쉘 스크립트(shell script)라고 부르는데, 언뮤보면 파일 형태의 작은 프로그램이라고 할 수 있다.
6.1 쉘 스크립트 기초
6.1.1 첫 번째 스크립트 작성 - 직접 만들어보자!
이론만 붐 재미없니기 차라리 바로 만들어보자. molecules/
디렉토리에서 우리의 첫 번째 스크립트 middle.sh
를 탄생시킬 예정이다. 이름에서 짐작할 수 있듯이, 파일의 ’중간부분’을 추출하는 스크립트다.
$ cd molecules
$ nano middle.sh
나노(nano) 편집기를 사용해서 middle.sh
파일을 열어보자. 아직 존재하지 않는 파일이라도 걱정 안 해도 된다 - 나노가 알아서 새로 만들어주거든! 편집기가 열리면 다음 내용을 입력해보자:
head -n 15 octane.pdb | tail -n 5
아하! 이게 바로 앞서 배웠던 파이프라인의 변주곡이다. octane.pdb
파일에서 11번째부터 15번째 줄까지, 즉 중간 5줄을 쿼 내오는 마법이다.
여기서 중요한 포인트는 명령어를 직접 실행하는 게 아니라 파일에 ’저장’한다는 점이다. 마치 요리 레시피를 수첩에 적어두는 것처럼 말이다.
이제 저장할 시간! 나노에서는 Ctrl-O
(영문 O)를 눌러 파일을 저장하고, Ctrl-X
로 편집기를 나가면 된다. 마치 마이크로소프트 워드의 Ctrl-S
와 Alt-F4
같은 개념이다. 나가서 나서 ls
를 코 한 번 날려보면 우리의 소중한 middle.sh
파일이 무사히 생성되어 있을 거다.
6.1.2 스크립트 실행 - 드디어 마법의 순간!
자, 이제 우리의 첫 작품을 실행해볼 시간이다! 파일에 저장해둔 명령어들이 어떻게 마법을 부리는지 보자. bash
쉘을 사용하고 있으니까, 다음처럼 입력하면 된다:
$ bash middle.sh
ATOM 9 H 1 -4.502 0.681 0.785 1.00 0.00
ATOM 10 H 1 -5.254 -0.243 -0.537 1.00 0.00
ATOM 11 H 1 -4.357 1.252 -0.895 1.00 0.00
ATOM 12 H 1 -3.009 -0.741 -1.467 1.00 0.00
ATOM 13 H 1 -3.172 -1.337 0.206 1.00 0.00
와우! 보이는가? 파이프라인을 내손으로 직접 치는 것과 똑같은 결과가 나오지 않는가! 이게 바로 쉘 스크립트의 마법이다. 한 번 작성해두면 몇 번이든 또같이 실행할 수 있다.
마이크로소프트 워드나 리브레오피스 Writer
를 “텍스트 편집기”라고 부르기도 한다. 하지만 프로그래밍을 할 때는 주의가 필요하다. 마이크로소프트 워드는 기본적으로 .docx
파일 형식으로 텍스트와 함께 글꼴, 제목 등의 서식 정보도 저장한다.
이런 추가 정보는 일반 텍스트가 아니어서 head
같은 도구가 이해할 수 없다. 이런 도구들은 입력 파일에 문자, 숫자, 키보드 특수문자만 있다고 가정한다. 따라서 프로그램을 작성할 때는 일반 텍스트 편집기를 사용하거나 일반 텍스트 형식으로 파일을 저장해야 한다.
6.2 스크립트 변수와 인자 처리
6.2.1 변수와 인자 기초 - 스크립트를 똑똑하게 만들기
매번 다른 파일의 행을 선택하려면 어떻게 할까? 매번 middle.sh
를 편집하여 파일명을 바꾸는 것은 비효율적이다. 대신 스크립트를 더 유연하게 만들어 보자:
$ nano middle.sh
nano
편집기로 octane.pdb
를 $1
이라는 특수 변수로 변경하자.
head -n 15 "$1" | tail -n 5
쉘 스크립트에서 $1
은 첫 번째 명령행 인자를 나타낸다. 이제 스크립트를 다음과 같이 실행할 수 있다.
$ bash middle.sh octane.pdb
ATOM 9 H 1 -4.502 0.681 0.785 1.00 0.00
ATOM 10 H 1 -5.254 -0.243 -0.537 1.00 0.00
ATOM 11 H 1 -4.357 1.252 -0.895 1.00 0.00
ATOM 12 H 1 -3.009 -0.741 -1.467 1.00 0.00
ATOM 13 H 1 -3.172 -1.337 0.206 1.00 0.00
혹은 다음과 같이 다른 파일에 대해 스크립트 프로그램을 실행해 보자.
$ bash middle.sh pentane.pdb
ATOM 9 H 1 1.324 0.350 -1.332 1.00 0.00
ATOM 10 H 1 1.271 1.378 0.122 1.00 0.00
ATOM 11 H 1 -0.074 -0.384 1.288 1.00 0.00
ATOM 12 H 1 -0.048 -1.362 -0.205 1.00 0.00
ATOM 13 H 1 -1.183 0.500 -1.412 1.00 0.00
파일명에 공백이 포함될 경우를 대비해 $1
을 따옴표로 감싼다. 이는 루프에서 변수를 따옴표로 감싸는 것과 같은 이유이다.
어, 그런데 아직도 한 가지 불편한 점이 남아있다. 중간 부분의 ’범위’를 바꾸고 싶을 때마다 여전히 스크립트를 편집해야 한다는 것이다. 11-15번째 줄이 아니라 5-10번째 줄을 보고 싶다르 문제다.
더 남다른 아이디어: 인자 3종 세트!
이제 3개의 매개변수를 활용해서 완전히 유연한 스크립트를 만들어보자. $1
은 파일명, $2
는 마지막 줄 번호, $3
은 추출할 줄 수로 사용할 예정이다.
이번에는 head
와 tail
명령어에 직접 숫자를 집어넣는 대신 $2
와 $3
변수를 사용해보자:
$ nano middle.sh
head -n "$2" "$1" | tail -n "$3"
이제 다음을 실행시킨다.
$ bash middle.sh pentane.pdb 15 5
ATOM 9 H 1 1.324 0.350 -1.332 1.00 0.00
ATOM 10 H 1 1.271 1.378 0.122 1.00 0.00
ATOM 11 H 1 -0.074 -0.384 1.288 1.00 0.00
ATOM 12 H 1 -0.048 -1.362 -0.205 1.00 0.00
ATOM 13 H 1 -1.183 0.500 -1.412 1.00 0.00
신기하지 않는가? 같은 스크립트를 사용하면서도 인자만 다르게 주면 완전히 다른 동작을 할 수 있다!
$ bash middle.sh pentane.pdb 20 5
ATOM 14 H 1 -1.259 1.420 0.112 1.00 0.00
ATOM 15 H 1 -2.608 -0.407 1.130 1.00 0.00
ATOM 16 H 1 -2.540 -1.303 -0.404 1.00 0.00
ATOM 17 H 1 -3.393 0.254 -0.321 1.00 0.00
TER 18 1
스크립트가 정상동작하지만, 다른 사람이 코드를 볼 때 목적을 이해하기 어렵다. 스크립트 상단에 주석(comments)을 추가하여 이 문제를 해결할 수 있다.
$ nano middle.sh
# 파일 중간에서 행(라인)을 추출한다.
# 사용법: bash middle.sh 파일명 마지막줄 라인수
head -n "$2" "$1" | tail -n "$3"
#
문자로 시작하여 행의 끝까지 주석으로 처리됩니다- 컴퓨터는 주석을 무시하지만, 사람이 코드를 이해하는 데 필수적입니다
- 코드 수정 시 주석도 함께 업데이트해야 합니다
- 잘못된 주석은 주석이 없는 것보다 더 해로울 수 있습니다
6.2.2 모든 인자 처리 - $@
의 신비한 힘
지금까지는 파일 하나씩 처리했지만, 여러 파일을 한 번에 훅~ 처리하고 싶을 때도 있지 않는가? 예를 들어, 바닷가에서 수집한 30개의 .pdb
파일들을 크기순으로 정렬해서 보고 싶다고 하자. 아, 생각만 해도 지겨운데!
$ wc -l *.pdb | sort -n
여기서 wc -l
은 파일별 줄 수를 세어주는 매우 편리한 도구이고(wc
는 ’word count’의 줄임말, -l
은 ’lines를 세어라’는 뜻), sort -n
은 숫자 크기 순으로 예쁘게 정렬해준다.
하지만 여기서 문제가 생긴다. 이 명령어를 그냥 스크립트에 넣으면 오직 현재 디렉토리의 .pdb
파일들만 처리된다는 점이다.
.pdb 파일이나 .txt 파일이나 .csv 파일이나… 다양한 파일 형식에 적용하고 싶다면?
여기서 문제가 생긴다. $1
, $2
, $3
처럼 하나씩 지정할 수도 없다. 파일이 몇 개인지 미리 알 수 없기 때문이다. 오늘은 5개, 내일은 50개일 수도 있고 말이다.
이럴 때 등장하는 것이 바로 $@
라는 신비한 변수다! 이것은 “사용자가 스크립트에게 전달한 모든 인자들”을 한 껴으로 나타내는 마법 같은 도구다. 마치 상자 하나에 모든 걸 담아두는 것처럼!
그리고 파일명에 공백이 들어있는 것도 고려해야 하니 따옴표로 감싸서 "$@"
형태로 사용하는 게 안전하다.
이제 열심히 만들어보자!
$ nano sorted.sh
# 길이로 파일명을 정렬
# Usage: bash sorted.sh 파일명_1개_이상
wc -l "$@" | sort -n
자, 이제 우리의 걸작 sorted.sh
를 실행해보자! 다양한 파일들을 한 번에 처리하는 마법을 보자:
$ bash sorted.sh *.pdb ../creatures/*.dat
9 methane.pdb # 가장 작은 분자!
12 ethane.pdb
15 propane.pdb
20 cubane.pdb
21 pentane.pdb
30 octane.pdb # PDB 파일들 중에는 가장 크고
163 ../creatures/basilisk.dat # 이것들은 완전 다른 데이터!
163 ../creatures/minotaur.dat
163 ../creatures/unicorn.dat
596 total # 전체 줄 수 합계
6.2.3 CSV 데이터 처리 예제
이제 $@
변수를 활용한 더 실용적인 예제를 만들어보자. 생물학 연구에서 자주 사용하는 CSV 형태의 동물 관찰 데이터를 처리하는 스크립트를 작성해보자. 다음과 같은 형식의 데이터 파일이 있다고 가정하자.
2013-11-05,deer,5
2013-11-05,rabbit,22
2013-11-05,raccoon,7
2013-11-06,rabbit,19
2013-11-06,deer,2
2013-11-06,fox,1
2013-11-07,rabbit,18
2013-11-07,bear,1
각 줄은 날짜, 종, 개체수 형식이다. 여러 CSV 파일에서 관찰된 고유한 종을 추출하려면 다음 명령어를 사용할 수 있다.
$ cut -d , -f 2 animals.csv | sort | uniq
하지만 매번 이 긴 명령어를 입력하는 대신, 여러 파일을 한 번에 처리하는 스크립트를 만들어보자.
$ nano species.sh
#!/bin/bash
# CSV 파일에서 고유 종을 출력하는 스크립트
# 사용법: bash species.sh file1.csv file2.csv ...
for file in "$@"
do
echo "$file 파일의 고유 종."
# 두 번째 필드(종 이름)를 추출하여 중복 제거
cut -d , -f 2 "$file" | sort | uniq
echo
done
이 스크립트를 실행하면 결과가 다음과 같이 출력된다.
$ bash species.sh animals1.csv animals2.csv
animals1.csv 파일의 고유 종.
bear
deer
fox
rabbit
raccoon
animals2.csv 파일의 고유 종.
deer
fox
rabbit
wolf
여기서 중요한 점은 for
루프와 "$@"
변수의 조합이다. 이는 스크립트에 전달된 모든 파일을 순회하면서 각각 처리할 수 있게 해준다.
스크립트에 파일명을 제공하지 않으면 무엇이 일어날까? 예를 들어 다음과 같이 실행한다면,
$ bash sorted.sh
*.dat
같은 파일 패턴을 제공하지 않으면 $@
이 비어있게 되어 스크립트 내부의 파이프라인은 다음과 같이 된다:
$ wc -l | sort -n
파일명이 없으므로 wc
는 표준 입력을 기다린다. 따라서 쉘은 사용자가 데이터를 입력할 때까지 대기하게 된다. 사용자에게는 스크립트가 멈춘 것처럼 보인다.
6.2.4 스크립트 변수 동작 이해하기
스크립트 변수의 동작 방식을 실제 예제로 이해해보자. molecules
디렉토리에 다음과 같은 간단한 script.sh
파일이 있다고 가정하자.
head -n $2 $1
tail -n $3 $1
이 스크립트를 다음과 같이 실행한다면 어떤 결과가 나올까?
bash script.sh '*.pdb' 1 1
그림 6.2 에서 보여주는 과정을 살펴보자. 스크립트 실행 시 세 단계를 거친다.
Step 1 (인수 할당): 명령행 인수들이 스크립트 변수에 할당된다. $1 = '*.pdb'
, $2 = 1
, $3 = 1
로 설정된다.
Step 2 (변수 치환): 스크립트 내부에서 변수들이 실제 값으로 치환된다. head -n 1 '*.pdb'
와 tail -n 1 '*.pdb'
형태가 된다.
Step 3 (패턴 확장): 따옴표가 제거되면서 *.pdb
패턴이 실제 파일명들로 확장된다. 최종적으로 head -n 1 *.pdb
와 tail -n 1 *.pdb
가 실행된다.
핵심은 따옴표의 역할이다. '*.pdb'
에 따옴표가 있기 때문에 쉘이 명령행에서 패턴을 확장하지 않고, 스크립트 내부에서 확장한다. 따옴표가 없었다면 $1
에는 첫 번째 .pdb
파일명만 할당되어 한 파일만 처리되었을 것이다.
결과적으로 모든 .pdb
파일의 첫 줄과 마지막 줄이 출력된다. 이 예제는 쉘 변수 치환과 패턴 확장의 순서가 얼마나 중요한지 보여준다.
6.3 명령 히스토리와 디버깅
6.3.1 명령 히스토리 재사용
유용한 명령어 시리즈를 방금 실행했다고 가정해보자. 예를 들어 논문에 사용할 그래프를 생성했다면, 나중에 그래프를 다시 생성해야 할 수도 있어서 명령어를 파일에 저장하고 싶다. 명령어를 다시 입력하는 대신(오타가 발생할 수도 있다) 다음과 같이 할 수 있다:
$ history | tail -n 5 > redo-figure-3.sh
redo-figure-3.sh
파일에는 이제 다음 내용이 있다:
297 bash goostats NENE01729B.txt stats-NENE01729B.txt
298 bash goodiff stats-NENE01729B.txt /data/validated/01729.txt > 01729-differences.txt
299 cut -d ',' -f 2-3 01729-differences.txt > 01729-time-series.txt
300 ygraph --format scatter --color bw --borders none 01729-time-series.txt figure-3.png
301 history | tail -n 5 > redo-figure-3.sh
여기서 흥미로운 점이 하나 있다. 생성된 파일의 마지막 명령어가 history
명령 자체라는 것이다. 이는 쉘이 명령어를 실제로 실행하기 전에 명령 로그에 먼저 기록하기 때문이다.
왜 쉘은 명령어를 실행하기 전에 기록할까? 명령어가 오류로 중단되거나 실패할 경우, 어떤 명령어에서 문제가 발생했는지 파악하는 것이 중요하다. 만약 명령어가 실행된 후에 기록한다면 문제가 발생한 마지막 명령어에 대한 기록이 남지 않을 것이다.
편집기에서 명령어 번호를 제거하고 history
명령어가 포함된 마지막 행을 삭제한 후에는 그림 생성 과정에 대한 정확한 기록을 얻을 수 있다.
실무에서는 대부분 쉘 프롬프트에서 명령어를 몇 번 실행해 정상 작동을 확인한 후 재사용을 위해 파일로 저장한다.
이런 작업 방식은 데이터 분석 작업흐름(workflow)에서 발견한 내용을 history
를 통해 재사용하는 전형적인 패턴이다. 약간의 편집 작업을 거쳐 정리한 후 쉘 스크립트 파일로 저장한다.
6.3.2 다양한 스크립트 패턴 분석
스크립트 변수의 동작 원리를 이해했으니, 이제 실제로 다양한 패턴의 스크립트가 어떻게 작동하는지 분석해보자. alkanes
디렉토리에 여러 .pdb
파일이 있다고 가정하고, 세 가지 다른 스크립트를 살펴보자.
스크립트 1: 패턴 무시하기
# script1.sh 내용
echo *.*
이 스크립트를 bash script1.sh *.pdb
로 실행하면:
- 스크립트는 전달받은 인자(
*.pdb
파일들)를 완전히 무시한다 - 대신 현재 디렉토리에서 점(
.
)이 포함된 모든 파일을 출력한다 - 결과:
cubane.pdb ethane.pdb methane.pdb octane.pdb pentane.pdb propane.pdb
흥미로운 점은 스크립트에 전달된 인자가 실제로는 사용되지 않는다는 것이다. 이는 실수로 만들어질 수 있는 혼동의 원인이다.
스크립트 2: 제한된 인자 처리
# script2.sh 내용
for filename in $1 $2 $3
do
cat $filename
done
이 스크립트를 bash script2.sh *.pdb
로 실행하면:
- 명령행의
*.pdb
가 먼저 확장되어 여러 파일명이 전달된다 - 하지만 스크립트는 처음 3개의 인자(
$1
,$2
,$3
)만 사용한다 - 각 파일의 전체 내용을 순서대로 출력한다
- 4번째 이후의 파일들은 무시된다
예를 들어 6개의 .pdb
파일이 있어도 처음 3개만 처리된다.
스크립트 3: 파일명 조작
# script3.sh 내용
echo $@.pdb
이 스크립트를 bash script3.sh *.pdb
로 실행하면:
$@
는 전달된 모든 인자를 나타낸다- 하지만
.pdb
가 각 파일명에 추가로 붙는다 - 결과:
cubane.pdb.pdb ethane.pdb.pdb methane.pdb.pdb ...
이는 의도하지 않은 결과로, .pdb
확장자가 중복된다. 이런 실수는 실제 스크립트 작성에서 자주 발생한다.
이 세 가지 예제를 통해 스크립트 작성 시 자주 발생하는 실수들을 확인할 수 있다. 첫 번째 스크립트처럼 전달받은 인자를 사용하지 않는 경우, 사용자는 특정 파일들을 처리하려고 했지만 실제로는 모든 파일이 대상이 된다. 두 번째 스크립트는 $1
, $2
, $3
만 사용하여 처리할 수 있는 파일 수가 제한되는 문제를 보여준다. 많은 파일을 처리하려면 $@
를 활용한 루프가 더 적절하다. 세 번째 스크립트는 문자열 조작 시 의도치 않은 중복이 발생할 수 있음을 경고한다.
이런 패턴들을 미리 이해하고 있으면 실무에서 스크립트의 동작을 빠르게 예측하고, 잠재적인 문제를 사전에 방지할 수 있다. 특히 파일 처리 스크립트를 작성할 때는 전달받은 인자를 어떻게 활용할 것인지, 몇 개의 파일까지 처리할 것인지, 파일명을 어떻게 조작할 것인지를 신중히 고려해야 한다.
6.3.3 디버깅 기법과 오류 처리
스크립트를 작성하다 보면 예상대로 작동하지 않는 경우가 자주 발생한다. 넬(Nelle) 박사가 겪은 실제 사례를 통해 스크립트 디버깅 방법을 배워보자.
문제 상황 발생
넘 박사가 north-pacific-gyre
디렉토리에 데이터 분석용 스크립트를 작성했다:
# do-errors.sh 내용
# 데이터 파일별로 통계량 계산
for datafile in "$@"
do
echo $datfile
bash goostats $datafile stats-$datafile
done
이 스크립트를 다음과 같이 실행했다:
$ bash do-errors.sh NENE*A.txt NENE*B.txt
그런데 아무 출력도 나오지 않았다. 분명히 여러 개의 데이터 파일이 있는데 왜 아무것도 처리되지 않는 걸까?
디버깅 방법: -x 옵션 활용
이런 상황에서는 bash의 디버그 모드를 사용할 수 있다. -x
옵션을 추가하면 실행되는 명령어를 하나씩 보여준다:
$ bash -x do-errors.sh NENE*A.txt NENE*B.txt
+ for datafile in "$@"
+ echo
+ bash goostats NENE01729A.txt stats-NENE01729A.txt
+ for datafile in "$@"
+ echo
+ bash goostats NENE01729B.txt stats-NENE01729B.txt
출력을 보면 echo
명령어가 아무것도 출력하지 않는 것을 확인할 수 있다.
오류 발견
문제를 자세히 살펴보니 타이핑 오류가 있었다:
- 스크립트에서는
echo $datfile
(잘못) - 루프 변수는
$datafile
(올바름)
변수명 datfile
은 존재하지 않아서 빈 문자열을 반환한 것이다.
올바른 스크립트
# do-errors.sh 수정 버전
# 데이터 파일별로 통계량 계산
for datafile in "$@"
do
echo $datafile # 올바른 변수명
bash goostats $datafile stats-$datafile
done
수정 후 실행하면:
$ bash do-errors.sh NENE*A.txt NENE*B.txt
NENE01729A.txt
NENE01729B.txt
실무에서 스크립트 디버깅을 효과적으로 수행하려면 몇 가지 기본적인 접근법을 알아두는 것이 좋다. 먼저 변수가 예상한 값을 가지고 있는지 확인하기 위해 echo
문을 활용한다. 변수의 실제 값을 출력해보면 예상과 다른 결과의 원인을 쉽게 파악할 수 있다. 복잡한 스크립트의 경우 전체를 한 번에 실행하지 말고 부분별로 나누어 테스트하는 것이 효율적이다.
bash가 제공하는 오류 메시지도 문제 해결의 중요한 단서가 된다. 처음에는 이해하기 어려울 수 있지만, 메시지를 주의 깊게 읽어보면 대부분 문제의 위치와 원인을 알려준다. 가장 흔하면서도 찾기 어려운 실수는 변수명이나 명령어명의 오타다. 이런 사소한 실수가 스크립트 전체를 작동하지 않게 만들 수 있으므로 항상 주의해야 한다.
이런 간단한 실수들이 스크립트가 작동하지 않는 주요 원인이다. 체계적인 디버깅 접근법을 익히면 문제를 빠르게 찾아 해결할 수 있다.
6.4 넬 박사 스크립트 도전
바다 한 가운데서 수집한 소중한 데이터들과 씨름하고 있는 넬(Nelle) 박사에게 큰 고민이 생겼다. 지도교수가 “모든 분석 과정이 완벽하게 재현 가능해야 한다”며 압박을 가하고 있는 것이다. 논문 심사에서 “이 결과를 어떻게 얻었는지 정확히 보여달라”고 요구받을 수 있다는 것이다.
넬 박사는 고민 끝에 깨달았다. 분석 과정을 일일이 기억할 수는 없지만, 스크립트로 모든 단계를 기록해두면 언제든지 똑같은 결과를 재현할 수 있다는 것을!
첫 번째 시도: 자동화 시작
넬 박사는 자신의 프로젝트 디렉토리로 이동해서 첫 번째 자동화 스크립트를 만들기 시작했다.
$ cd ../../north-pacific-gyre/
$ nano do-stats.sh
몇 시간의 고민 끝에, 넬 박사는 다음과 같은 나름 괜찮은 솔루션을 완성했다.
# 데이터 파일별로 통계량 계산 - 넘 박사의 걸작!
for datafile in "$@"
do
echo "처리 중: $datafile"
bash goostats.sh $datafile stats-$datafile
done
마법의 순간
이제 넬 박사는 복잡했던 첫 번째 분석 단계를 단 한 줄로 실행할 수 있게 되었다.
$ bash do-stats.sh NENE*[AB].txt
정말 놀라운 건, 처리된 파일의 개수만 궁금할 때도 파이프라인과 조합할 수 있다는 점이었다.
$ bash do-stats.sh NENE*[AB].txt | wc -l
이렇게 하면 파일 이름 대신 숫자만 깔끔하게 출력된다!
설계 철학의 고민
넬 박사가 이 스크립트에서 특별히 고민한 부분이 있다. ‘사용자가 어떤 파일을 처리할지 직접 결정하도록 할 것인가, 아니면 스크립트가 알아서 정해진 파일들만 처리하도록 할 것인가?’
만약 다음처럼 파일을 하드코딩했다면 명확하다.
# Site A, Site B 데이터만 고정으로 처리
for datafile in NENE*[AB].txt
do
echo $datafile
bash goostats $datafile stats-$datafile
done
장단점 트레이드오프
이런 접근법의 장점은 분명하다.
- 항상 올바른 파일만 처리한다
- 문제가 있는 ‘Z’ 파일을 실수로 포함시킬 걱정이 없다
- 실행할 때마다 일관된 결과를 보장한다
하지만 단점도 무시할 수 없다.
- 유연성이 떨어진다 - 다른 데이터셋에는 사용할 수 없다
- Z 파일도 함께 분석하고 싶을 때는 스크립트를 수정해야 한다
- 새로운 실험 조건에 맞추려면 매번 코드를 바꿔야 한다
넬 박사의 현명한 선택
결국 넬 박사는 첫 번째 방법을 선택했다. 사용자가 원하는 파일을 직접 지정할 수 있도록 하는 것이다. 물론 더 고급 기능을 추가할 수도 있다 - 매개변수가 제공되지 않으면 자동으로 기본값 NENE*[AB].txt
를 사용하도록 말이다.
이는 프로그래밍에서 항상 마주치는 영원한 딜레마다: 유연성 vs 복잡성. 더 유연하게 만들수록 코드는 복잡해지고, 더 단순하게 만들수록 활용도는 제한된다. 넬 박사는 자신의 연구 스타일에 맞는 적절한 균형점을 찾은 것이다.
6.5 AI 스크립트 미래
지금까지 배운 전통적인 쉘 스크립트 기법들은 여전히 강력하고 유용하다. 하지만 여기서 한 걸음 더 나아가면, AI 도구들과의 협업을 통해 완전히 새로운 차원의 자동화가 가능해진다. 현대의 개발자들은 더 이상 혼자서 모든 코드를 작성하지 않는다. Claude Code나 GitHub Copilot CLI 같은 AI 도구들이 자연어로 된 요구사항을 즉석에서 완성된 스크립트로 변환해준다.
예를 들어, 단순히 “모든 .log 파일을 찾아서 100MB 이상인 것들을 압축해줘”라고 말하면 AI가 다음과 같은 완성된 스크립트를 만들어준다:
#!/bin/bash
find . -name "*.log" -size +100M -exec gzip {} \; -print |
while read file; do
echo "압축 완료: $file"
done
심지어 오류 처리, 진행 상황 표시, 사용법 안내까지 포함해서 말이다.
이는 넬 박사가 몇 시간에 걸쳐 고민했던 스크립트 설계 과정을 몇 분 안에 완성할 수 있다는 뜻이다. 기본기가 탄탄한 여러분이 AI의 도움을 받으면, 상상했던 것보다 훨씬 정교하고 견고한 자동화 솔루션을 만들어낼 수 있다.
핵심은 협업이다
AI가 모든 것을 대신해주는 게 아니라, 여러분이 배운 쉘 스크립트의 기본 원리와 AI의 생성 능력이 만나 시너지를 낸다. 여러분은 “무엇을”, “왜” 해야 하는지 알고 있고, AI는 “어떻게” 구현할지 도와준다.
이제 여러분도 반복 작업에 지친 연구자든, 대용량 데이터를 다뤄야 하는 분석가든, 시스템을 관리해야 하는 엔지니어든 - 어떤 상황에서든 쉘 스크립트와 AI의 힘을 결합해 놀라운 자동화 솔루션을 만들어낼 수 있는 현대적 개발자가 되었다!