Linux 강좌 / Bash 셸 스크립트 만들기: 변수·조건문·반복문
셸 스크립트는 터미널에서 실행할 명령과 제어 흐름을 파일에 담아 반복 실행하는 프로그램입니다. Bash에서는 변수 확장을 큰따옴표로 감싸고, 입력 조건과 종료 상태를 확인하면서 작은 스크립트부터 만드는 것이 좋습니다.
이 글은 명령을 파일에 저장해 실행하는 첫 프로그램을 만듭니다. 변수·종료 상태·연산자 원리는 셸 실행 흐름을, 실패·경로·동시 실행의 한계는 Bash 오류 처리와 안전한 파일 작업을 참고하세요.
Bash와 sh를 구분하고 작업 폴더 준비하기
이 실습은 Ubuntu·Rocky Linux의 Bash를 대상으로 합니다. [[ ... ]]와 배열 같은 Bash 기능은 다른 sh에서 동작하지 않을 수 있으므로 sh script.sh로 실행하지 마세요. 먼저 bash --version으로 설치 여부를 확인하고, 새 디렉터리에서 파일을 만듭니다. 편집기의 줄바꿈은 LF로 저장합니다.
lab=$(mktemp -d "$HOME/linux-script-lab.XXXXXX")
cd "$lab"
mkdir "sample files"
printf "one\n" > "sample files/alpha.txt"
printf "two\n" > "sample files/beta.txt"
실행 가능한 전체 예제 작성하기
아래 블록을 그대로 실행하면 현재 디렉터리에 list-files.sh가 생성됩니다. 인수로 받은 디렉터리를 확인하고 그 안의 일반 파일 이름을 한 줄씩 출력합니다. 파일을 만들거나 삭제하는 스크립트가 아니므로 변수·분기·반복 동작을 비교하기에 적합합니다.
cat > list-files.sh <<'EOF'
#!/usr/bin/env bash
if (( $# != 1 )); then
printf "Usage: %s DIRECTORY\n" "$0" >&2
exit 2
fi
directory=$1
if [[ ! -d "$directory" ]]; then
printf "Not a directory: %s\n" "$directory" >&2
exit 1
fi
count=0
for path in "$directory"/*; do
[[ -f "$path" ]] || continue
printf "%s\n" "${path##*/}"
count=$((count + 1))
done
printf "files=%s\n" "$count"
EOF
bash -n list-files.sh
bash list-files.sh "sample files"
예상 출력은 alpha.txt, beta.txt, files=2입니다. 파일명의 정렬 순서는 로케일의 영향을 받을 수 있습니다. bash -n은 문법만 검사하므로 잘못된 경로나 권한 같은 실행 오류까지 찾지는 못합니다. 스크립트 앞의 #!는 실행기를 지정하는 shebang이며 명령 앞에 붙이는 터미널 프롬프트가 아닙니다.
변수와 따옴표가 파일 경로를 지키는 이유
변수 대입은 directory=$1처럼 등호 주위에 공백 없이 씁니다. 읽을 때는 "$directory"처럼 큰따옴표를 사용해 공백이 있는 경로를 한 인수로 전달합니다. 작은따옴표는 보통 변수 확장을 막고, 큰따옴표는 변수 확장을 허용하면서 단어 분리와 파일명 확장을 막습니다. eval로 입력을 명령처럼 다시 해석할 필요는 없습니다.
"$directory"/*에서는 경로 부분을 보호하고 별표만 파일명 확장에 사용합니다. 기본 Bash 설정에서 매칭이 없어도 패턴 문자열이 남을 수 있으므로 [[ -f "$path" ]] || continue로 건너뜁니다. 이 패턴은 점으로 시작하는 숨김 파일을 포함하지 않고, 디렉터리 아래를 재귀 탐색하지 않으며, 일반 파일을 가리키는 심볼릭 링크는 -f가 참일 수 있습니다.
인수·조건문·반복문과 종료 상태 읽기
| 구문 | 의미 |
|---|---|
| $# / $1 | 인수 개수 / 첫 번째 인수 |
| [[ -d ... ]] | 대상이 디렉터리인지 검사 |
| for ...; do ...; done | 확장된 각 항목에 같은 처리 적용 |
| ${path##*/} | 마지막 슬래시 앞부분을 제거해 이름만 얻음 |
| exit 0 / 1 / 2 | 이 예제의 성공 / 입력 대상 오류 / 사용법 오류 |
마지막 printf가 성공하면 이 스크립트는 상태 0으로 끝납니다. 종료 코드 1과 2의 구체적인 의미는 이 스크립트에서 정한 약속이며 모든 명령에 동일하게 적용되는 분류는 아닙니다. $?는 바로 직전 명령의 종료 상태이므로 다른 명령을 끼우기 전에 확인합니다.
bash list-files.sh
printf "exit=%s\n" "$?"
bash list-files.sh missing-directory
printf "exit=%s\n" "$?"
chmod u+x list-files.sh
./list-files.sh "sample files"
한 조건씩 바꾸어 결과 비교하기
- 먼저 정상 디렉터리로 실행해 파일 수 2를 확인합니다.
- sample files에 gamma.txt를 하나 추가하고 다시 실행해 3이 되는지 확인합니다.
- 추가한 파일만 제거하고 다시 2로 돌아오는지 확인합니다.
- 빈 디렉터리에서는 files=0, 없는 경로에서는 오류와 종료 상태 1인지 확인합니다.
Windows에서 편집한 뒤 bash\r나 ^M 관련 오류가 나면 줄바꿈 형식을 확인합니다. 실행 권한이 없어도 bash 파일명으로 읽어 실행할 수 있지만, ./파일명은 실행 권한과 올바른 shebang이 필요합니다. 실습 파일을 정리하기 전 현재 경로를 확인하고 시스템 디렉터리에서 일괄 삭제 명령을 실행하지 마세요.
Windows 편집기에서 줄바꿈 표시가 CRLF라면 LF로 바꾸고 저장합니다. VS Code에서는 상태 표시줄의 CRLF → LF를 선택할 수 있습니다. GNU sed가 있는 실습 환경에서는 아래처럼 원본 복사 후 변환할 수도 있습니다. 대상은 이 실습에서 만든 list-files.sh만 지정하세요.
cp -i -- list-files.sh list-files.sh.crlf-backup
sed -i 's/\r$//' list-files.sh
bash -n list-files.sh
bash list-files.sh "sample files"
구문 확인은 출력 없이 상태 0, 원래 예제 파일 두 개만 있다면 실행 결과는 files=2가 예상됩니다. 되돌릴 때는 백업 내용이 올바른지 확인하고 cp -i -- list-files.sh.crlf-backup list-files.sh로 복원합니다.




