Bash 셸의 case 문은 C 언어의 switch 문과 비슷한 역할을 합니다. 정수나 문자처럼 단순한 값을 검사할 때 특히 유용하게 사용할 수 있습니다.
case 문은 반복문이 아닙니다. 코드 블록을 여러 번 실행하는 대신, 조건을 평가하고 프로그램의 흐름을 제어하는 분기문입니다.
이 글에서는 Bash case 명령을 실무에 바로 적용할 수 있는 5가지 예제와 함께 살펴보겠습니다.
Bash case 문의 기본 구조
Bash의 case 구문은 와일드카드 문자를 포함할 수 있는 패턴과 문자열을 비교할 수 있습니다. 사실상 case 문은 if-then-else 문을 더 단순하고 읽기 쉽게 만든 형태라고 볼 수 있습니다.
Bash case 문의 기본 문법은 다음과 같습니다.
case expression in
pattern1 )
statements ;;
pattern2 )
statements ;;
...
esac
case 문을 사용할 때 기억해야 할 핵심 사항은 다음과 같습니다.
- case 문은 먼저 표현식을 확장(expand)한 뒤, 각 패턴과 순서대로 비교합니다.
- 일치하는 패턴을 찾으면 이중 세미콜론(
;;) 직전까지의 모든 문장을 실행합니다. - 첫 번째 일치가 발생하면 case 문은 곧바로 종료되며, 종료 상태(exit status)는 마지막으로 실행된 명령의 결과를 따릅니다.
- 어떤 패턴과도 일치하지 않으면 case 문의 종료 상태는 0이 됩니다.
예제 1. 프로세스에 시그널 보내기
다음 스크립트는 시그널 번호와 프로세스 ID(PID)를 인자로 받아, 해당 프로세스에 시그널을 전송합니다.
$ cat signal.sh
#!/bin/bash
if [ $# -lt 2 ]
then
echo "Usage : $0 Signalnumber PID"
exit
fi
case "$1" in
1) echo "Sending SIGHUP signal"
kill -SIGHUP $2
;;
2) echo "Sending SIGINT signal"
kill -SIGINT $2
;;
3) echo "Sending SIGQUIT signal"
kill -SIGQUIT $2
;;
9) echo "Sending SIGKILL signal"
kill -SIGKILL $2
;;
*) echo "Signal number $1 is not processed"
;;
esac
위 스크립트의 동작 방식은 다음과 같습니다.
$1과$2는 각각 시그널 번호와 프로세스 ID를 의미합니다.- kill 명령을 사용해 지정된 프로세스 ID에 대응하는 시그널을 보냅니다.
- sleep 명령을 지정된 초만큼 실행합니다.
- 마지막의
*)비교는 기본(default) 케이스로, 어떤 값이든 일치합니다.
실행 방법: sleep 명령의 프로세스 ID를 찾은 다음, 그 PID에 kill 시그널을 보내 프로세스를 종료해 보겠습니다.
$ sleep 1000 $ ps -a | grep sleep 23277 pts/2 00:00:00 sleep $ ./signal.sh 9 23277 Sending SIGKILL signal $ sleep 1000 Killed
프로세스 종료와 관련해서는 이전에 정리한 "프로세스를 종료하는 4가지 방법" 글도 함께 참고해 보세요.
예제 2. 파일 내 패턴 매칭
이 예제는 주어진 패턴과 일치하는 줄 수와 단어 수를 출력하고, 옵션에 따라 패턴이 포함된 줄을 삭제합니다.
$ cat fileop.sh
#!/bin/bash
# 인자가 3개인지 확인 #
if [ $# -lt 3 ]
then
echo "Usage : $0 option pattern filename"
exit
fi
# 지정한 파일이 존재하는지 확인 #
if [ ! -f $3 ]
then
echo "Filename given \"$3\" doesn't exist"
exit
fi
case "$1" in
# 패턴과 일치하는 줄 수 세기
-i) echo "Number of lines matches with the pattern $2 :"
grep -c -i $2 $3
;;
# 패턴과 일치하는 단어 수 세기
-c) echo "Number of words matches with the pattern $2 :"
grep -o -i $2 $3 | wc -l
;;
# 일치하는 모든 줄 출력
-p) echo "Lines matches with the pattern $2 :"
grep -i $2 $3
;;
# 패턴과 일치하는 모든 줄 삭제
-d) echo "After deleting the lines matches with the pattern $2 :"
sed -n "/$2/!p" $3
;;
*) echo "Invalid option"
;;
esac
스크립트에서 사용할 샘플 텍스트 파일은 다음과 같습니다.
$ cat text Vim is a text editor released by Bram Moolenaar in 1991 for the Amiga computer. The name "Vim" is an acronym for "Vi IMproved" because Vim was created as an extended version of the vi editor, with many additional features designed to be helpful in editing program source code. Although Vim was originally released for the Amiga, Vim has since been developed to be cross-platform, supporting many other platforms. It is the most popular editor amongst Linux Journal readers.
패턴 "Vim"이 포함된 줄을 삭제한 후의 실행 결과입니다.
$ ./fileop.sh -d Vim text It is the most popular editor amongst Linux Journal readers.
파일 연산과 관련해 이전에 소개한 Bash ~ 확장과 { } 확장 글도 함께 참고하면 좋습니다.
예제 3. 확장자로 파일 유형 판별하기
이 예제는 파일명의 확장자를 기준으로 파일 유형(텍스트, C 소스 코드 등)을 출력합니다.
$ cat filetype.sh
#!/bin/bash
for filename in $(ls)
do
# 파일명에서 확장자 추출
ext=${filename##*\.}
case "$ext" in
c) echo "$filename : C source file"
;;
o) echo "$filename : Object file"
;;
sh) echo "$filename : Shell script"
;;
txt) echo "$filename : Text file"
;;
*) echo " $filename : Not processed"
;;
esac
done
$ ./filetype.sh
a.c : C source file
b.c : C source file
c1.txt : Text file
fileop.sh : Shell script
obj.o : Object file
text : Not processed
t.o : Object file
예제 4. 사용자에게 Yes/No 입력받기
소프트웨어 설치 과정에서 라이선스 동의 단계는 흔히 yes 또는 no 답변을 요구합니다. 다음 코드는 사용자에게 yes/no 입력을 받는 한 가지 방법을 보여줍니다.
$ cat yorno.sh
#!/bin/bash
echo -n "Do you agree with this? [yes or no]: "
read yno
case $yno in
[yY] | [yY][Ee][Ss] )
echo "Agreed"
;;
[nN] | [n|N][O|o] )
echo "Not agreed, you can't proceed the installation";
exit 1
;;
*) echo "Invalid input"
;;
esac
$ ./yorno.sh
Do you agree with this? [yes or no]: YES
Agreed
패턴이 파이프 문자(|)로 구분되어 여러 개 나열된 경우, 표현식이 그중 하나라도 일치하면 해당 문장들이 실행됩니다. 패턴은 순서대로 검사되며 일치하는 패턴을 찾으면 검사를 중단하고, 끝까지 일치하는 것이 없으면 아무 동작도 수행하지 않습니다.
배열 활용이 궁금하다면 이전에 정리한 "Bash 배열 예제 15가지" 글도 참고해 보세요.
예제 5. 서비스 시작/종료 스크립트
/etc/init.d 디렉터리의 시작 스크립트들은 애플리케이션을 시작(start)하거나 중지(stop)할 때 case 문을 활용합니다.
어떤 종류의 패턴이든 사용할 수 있지만, 정수나 문자처럼 원시(primitive) 값을 검사할 때는 case 문을 사용하는 것이 가장 깔끔합니다.
$ cat startpcapp #!/bin/bash case "$1" in 'start') echo "Starting application" /usr/bin/startpc ;; 'stop') echo "Stopping application" /usr/bin/stoppc ;; 'restart') echo "Usage: $0 [start|stop]" ;; esac $ ./startpcapp start Starting application /usr/bin/startpc started
추천 도서
Ramesh Natarajan의 『Bash 101 Hacks』. 필자는 Linux 환경에서 대부분의 시간을 보내는 만큼, Bash 커맨드 라인과 셸 스크립팅의 열렬한 애호가입니다. 15년 전 다양한 *nix 계열 시스템에서 작업할 때는 C 셸과 Korn 셸로 많은 코드를 작성했고, 이후 Linux 시스템 관리자로 일하면서 가능한 거의 모든 작업을 Bash 셸 스크립트로 자동화했습니다. 이러한 경험을 바탕으로, Bash 커맨드 라인과 셸 스크립팅의 실전 예제 101개를 담은 『Bash 101 Hacks』 eBook을 집필했습니다. Bash를 제대로 마스터하고 싶다면 이 책이 커맨드 라인과 셸 스크립팅을 자유자재로 다루는 데 큰 도움이 될 것입니다.