쉘 스크립트란? 리눅스 쉘과 쉘 스크립트 기초 - 변수, 조건문, 반복문, 함수 사용법
리눅스 쉘과 쉘 스크립트 기초 - 변수, 조건문, 반복문, 함수 사용법
리눅스나 유닉스에서 쉘(Shell)은 커널과 사용자를 이어주는 명령어 해석기예요. 터미널 창에서 명령어를 입력하면 쉘이 그 명령을 읽고 운영체제에 전달해줘요. 이번 포스팅에서는 리눅스 환경의 쉘과 쉘 스크립트에 대해 알아볼게요. 변수, 조건문(if, case), 반복문(for, while, until), 함수 같은 기초 개념부터 차근차근 설명해줄게요. 각 개념의 사용법은 물론이고, 특징과 장단점도 함께 다뤄보도록 해요.
리눅스 쉘이란? 쉘의 개념과 종류
쉘(Shell)은 사용자가 입력한 명령을 해석해주는 명령어 인터프리터예요. 쉽게 말해 터미널을 통해 운영체제와 대화하게 해주는 프로그램이지. 리눅스에서 기본으로 사용하는 CLI(Command Line Interface)가 바로 쉘이에요. 쉘을 사용하면 키보드로 명령어를 입력해 파일 관리나 프로그램 실행 같은 작업을 할 수 있어요.
대표적인 쉘의 종류로 sh(Bourne Shell)와 bash(Bourne Again Shell)가 있어요. sh는 유닉스 초창기부터 쓰인 기본 쉘이고,
bash는 그걸 개선해서 만든 쉘로 리눅스 대부분의 배포판에서 기본 쉘로 사용돼요. 이 외에도 csh(C Shell), ksh(Korn Shell), zsh(Z Shell) 같은 다양한 쉘이 있어요. 각 쉘마다 문법이나 기능에 조금씩 차이가 있지만 기본적인 명령어 사용법은 비슷해요. 예를 들어 macOS 최신 버전은 기본 쉘로 zsh를 사용하지만, 사용법은 bash와 거의 유사해요.
특징: 쉘은 대화형 모드로 한 줄 한 줄 명령을 실행할 수도 있고, 여러 명령을 파일에 모아 스크립트 모드로 실행할 수도 있어요. 다른 프로그래밍 언어와 달리 컴파일 과정 없이 즉시 실행되는 것이 특징이에요. 또한 리눅스 시스템에 기본 내장되어 있어서 별도 설치 없이 사용 가능해요.
장점: 다양한 리눅스 명령어와 오픈소스 도구들을 조합해 생산성을 높일 수 있다는 게 큰 장점이에요. 복잡한 프로그램을 하나하나 짜는 대신, 쉘을 통해 여러 명령을 연결하면 빠르게 원하는 작업을 수행할 수 있거든요. 그리고 쉘 스크립트는 포터블(portable)해서 리눅스/유닉스 계열 시스템이면 대부분 그대로 실행할 수 있어요. 여러 운영체제(CentOS, Ubuntu 등)에서 특별한 수정 없이도 스크립트를 돌릴 수 있다는 건 편리하죠.
단점: 쉘마다 미묘한 문법 차이가 있어서 호환성 문제가 생길 수 있어요. 예를 들어 bash에서만 되는 문법을 사용한 스크립트를 다른 쉘(sh 등)로 실행하면 문제가 생길 수도 있어요. 또한 쉘은 주로 문자열 기반으로 동작하고 명령마다 새 프로세스를 생성하기 때문에, 복잡한 계산이나 대량 데이터 처리에는 성능이 떨어질 수 있어요. 마지막으로, 명령어 몇 줄로 시스템을 제어할 수 있다 보니 오류가 치명적일 수 있어요. 잘못된 명령을 스크립트에 넣으면 시스템 파일을 지운다든지 하는 실수를 저지를 위험도 있으니 조심해야 해요.
쉘 스크립트란 무엇인가?
쉘 스크립트(Shell Script)는 여러 명령어를 한 파일에 모아 놓은 텍스트 파일이에요. 이 파일을 실행하면 안에 적힌 명령어들이 순서대로 자동 실행돼요. 평소에 터미널에서 하나씩 치던 명령들을 차례로 적어두고 한 번에 실행하는 셈이죠. 흔히 확장자 .sh를 붙여서 스크립트 파일임을 표시하지만, 꼭 확장자가 필요한 건 아니에요
예를 들어, 아래처럼 간단한 쉘 스크립트를 만들어볼 수 있어요
#!/bin/bash
echo "Hello, world!"
첫 줄의 #!/bin/bash는 셰뱅(shebang)이라고 불리는데, 이 스크립트를 실행할 쉘 인터프리터를 지정해줘요. 위 예시는 bash 쉘로 실행하겠다는 의미예요. 이렇게 파일 첫 줄에 #!경로/쉘을 써주면 터미널에서 바로 실행할 때 해당 쉘이 이 파일을 처리해줘요. 스크립트를 저장한 후 터미널에서 chmod +x 파일이름.sh 명령으로 실행 권한을 주고, ./파일이름.sh 처럼 실행하면 스크립트 내용이 순서대로 실행돼요.
쉘 스크립트는 자동화된 작업이나 반복적인 작업을 간소화하는 데 널리 쓰여요. 예를 들어 여러 개의 파일을 한꺼번에 변환하거나, 백업 작업을 정기적으로 실행하거나, 시스템 초기 설정을 스크립트로 만들어서 한 번에 처리하는 식이죠.
- 장점: 손으로 일일이 하려면 번거로운 일들을 스크립트 한 번으로 처리할 수 있으니 관리 효율이 높아져요. 다른 언어에 비해 문법이 단순하고 OS 명령어를 그대로 활용하니 배우기 쉽고 빠르게 작성할 수 있어요. 별도 컴파일 과정 없이 바로 실행되니 결과 확인도 즉각적이에요. 또한 리눅스 기본 명령들을 조합해서 만들기 때문에, 작은 유틸리티를 빠른 개발이 가능해요.
- 단점: 복잡한 프로그램을 만들기에는 한계가 있어요. 디버깅이 어려운 편이라 긴 스크립트는 오류 찾기가 힘들고, 코드가 길어지면 가독성도 떨어져요. 또한 실행 속도가 컴파일된 언어보다 느리고, 너무 복잡한 데이터 처리나 GUI 같은 건 쉘 스크립트로 하기엔 적합하지 않아요. 그래서 주로 시스템 관리나 간단한 자동화에 초점을 맞추는 게 좋아요.
쉘 변수와 환경 변수 사용법
프로그래밍에서 변수(variable)는 값을 저장해두는 상자 같은 개념이죠. 쉘에서도 변수를 활용할 수 있어요. 쉘 스크립트의 변수는 특별한 타입 구분 없이 문자열로 취급되는 경우가 많아요 (숫자도 따로 지정하지 않으면 문자열로 간주돼요).
변수 선언과 할당: 쉘에서 변수를 만들 때는 이름=값 형태로 써요. 등호 주위에 공백을 넣으면 안 돼요. 예를 들어
name="홍길동"
echo $name # 출력: 홍길동
위처럼 name="홍길동"이라고 쓰면 name이라는 변수가 생기고 그 안에 "홍길동"이라는 값이 들어가요. 꺼내쓸 때는 $name처럼 변수 이름 앞에 $를 붙여요. (${name}처럼 중괄호로 감싸는 방식도 있어요. 복잡한 문자열에 섞어 쓸 때 유용해요.)
환경 변수(Environment Variable): 환경 변수는 말 그대로 시스템 환경에 영향을 주는 변수예요. 보통 대문자 이름을 가지고, 모든 프로세스가 공통으로 참조하거나 상속받는 값들이죠. 예를 들어 PATH는 실행 파일들을 찾는 경로 목록, HOME은 내 홈 디렉토리, USER는 현재 사용자 이름 등의 중요한 환경 변수가 기본으로 설정돼 있어요. 터미널에서 echo $PATH 같은 식으로 확인해볼 수 있어요.
환경 변수와 일반 변수의 차이는 전역성에 있어요. 내가 export한 환경 변수는 자식 프로세스(내가 실행하는 다른 프로그램이나 하위 쉘)에도 전달돼요. 반면 일반 변수는 현재 쉘이나 스크립트 안에서만 유효하고, 밖으로는 안 나가요. 쉘 스크립트 내에서 변수를 그냥 선언하면 스크립트가 끝날 때 사라지지만, export VAR=값으로 설정하면 그 스크립트를 실행한 쉘에서 환경 변수로 등록돼요.
export MYVAR="Hello"
이렇게 설정한 환경 변수 MYVAR는 이후에 실행하는 하위 프로세스에서도 $MYVAR로 접근 가능해요. (단, 새로운 쉘 세션을 열면 기본 환경 설정만 로드되고, 직접 export한 변수는 보통 사라져요. 지속적으로 쓰려면 ~/.bashrc나 ~/.profile에 넣어서 로그인 시 로드되게 해야 해요.)
- 장점: 변수 사용으로 명령어에 동적인 값을 줄 수 있고, 환경 변수로 시스템 전체 설정을 제어할 수 있어요. 환경 변수 PATH 설정만 바꿔도 실행 파일 경로를 추가하는 등 영향을 줄 수 있는 것처럼요. 또한 별도의 타입 선언 없이 바로 쓸 수 있어 쉽고 유연해요.
- 단점: 데이터 타입이 따로 없어서 문자열 파싱 문제가 생길 수 있어요. 예를 들어 숫자 계산도 문자열로 취급되기 때문에 따옴표나 백틱으로 감싸 expr 명령이나 $(( )) 구문을 써야 해요. 그리고 스크립트 내 변수와 환경 변수 이름이 중복되면 헷갈릴 수 있으니 주의해야 해요. 마지막으로, 띄어쓰기나 특수문자가 들어간 값을 다룰 때 따옴표로 묶지 않으면 예기치 않은 동작이 발생할 수 있어요. (예: 파일 이름에 공백이 있을 때 $var를 쓸 경우 등)
쉘 스크립트 조건문 사용법 (if문, case문)
프로그램에서 조건문은 특정 조건에 따라 다른 동작을 하게 만드는 구조죠. 쉘 스크립트에서도 if와 case로 조건문을 사용할 수 있어요.
if 조건문 사용하기
if 문은 조건식을 평가해서 참(true)이면 특정 명령을 실행하고 거짓(false)이면 넘어가는 구조예요. 쉘에서는 보통 test 명령이나 [ 브래킷 기호를 이용해 조건을 체크해요. 기본 형식은 이렇게 생겼어요
if [ 조건식 ]; then
# 조건식이 참일 때 실행할 명령들
elif [ 다른조건 ]; then
# 다른 조건이 참일 때 실행할 명령들
else
# 위 조건들이 모두 거짓일 때 실행할 명령들
fi
여기서 중요한 건 [와 ] 사이에 반드시 공백을 두고 조건을 써야 한다는 점이에요. ([는 사실 test라는 명령의 별칭이라서 그렇답니다.) 예를 들어 숫자 비교를 해볼게요
number=5
if [ $number -gt 10 ]; then
echo "number가 10보다 크다"
elif [ $number -eq 10 ]; then
echo "number는 10과 같다"
else
echo "number가 10보다 작다"
fi
위 스크립트를 실행하면 number가 10보다 작다를 출력하겠죠. -gt는 greater than (초과), -eq는 equal (같음) 같은 비교 연산자예요. 숫자 말고 문자열 비교를 할 때는 = 나 !=를 쓰고, 파일 존재 여부를 확인할 때는 [ -f filename ] (파일 존재 여부) 같은 파일 테스트 연산자를 쓸 수 있어요. 예를 들어
if [ -f "/etc/passwd" ]; then
echo "패스워드 파일이 존재합니다."
fi
이런 식으로 많이 활용해요.
case 문은 여러 경우를 비교해야 할 때 유용한 조건문이에요. 형태는 다음과 같아요
case "$변수" in
패턴1)
명령들 ;;
패턴2)
명령들 ;;
*)
명령들 ;;
esac
case는 변수의 값이나 문자열을 여러 패턴과 순서대로 매칭해봐요. 패턴1에 맞으면 그 부분을 실행하고 ;;에서 끝낸 뒤 esac로 case를 닫아요. *)는 위에 해당하는 패턴이 없을 때 실행되는 디폴트 부분이에요. 예를 들어 사용자의 입력에 따라 동작을 달리하고 싶을 때
read input # 사용자 입력을 변수 input에 받기
case "$input" in
[yY]|[yY][eE][sS])
echo "YES를 선택했어요." ;;
[nN]|[nN][oO])
echo "NO를 선택했어요." ;;
*)
echo "Yes나 No로 대답해 주세요." ;;
esac
위 예시는 yes/YES/y/YeS 등 여러 형태의 YES 입력을 모두 패턴으로 처리하고 있어요. 이런 식으로 if문으로 쓰면 복잡할 여러 조건을 case로 깔끔하게 정리할 수 있답니다.
- 장점: 조건문을 쓰면 스크립트에 의사결정 로직을 넣을 수 있어서, 상황에 따라 다른 명령을 실행하게 해줘요. if는 파일 존재 확인, 문자열 비교, 명령 실행 결과 체크(명령의 종료 상태 $? 활용) 등 다양하게 활용 가능해서 스크립트의 유연성을 높여줘요. case는 값에 따라 분기할 때 if/elif 연속으로 쓰는 것보다 훨씬 가독성 있게 여러 경우를 처리할 수 있다는 장점이 있어요.
- 단점: 쉘의 if문은 문법이 다른 언어보다 까다로울 수 있어요. 대괄호와 공백, 따옴표 처리 같은 규칙을 실수하면 예상치 못한 동작을 하거나 에러가 나요. 또, 복잡한 논리 조건을 구현하기에는 한계가 있어서, 너무 많은 조건이 들어가면 코드가 지저분해질 수 있어요. case문도 패턴 매칭을 하지만 정규표현식의 일부만 지원하는 등 한계가 있어서 복잡한 비교 논리는 외부 도구(grep 등)를 써야 할 때가 있어요
쉘 스크립트 반복문 사용법 (for, while, until)
반복문은 여러 번 반복 실행하기 위해 사용하는 구조예요. 쉘에서는 주로 for, while, until 세 가지 루프를 제공해요.
for 루프
for 문은 미리 정해진 리스트의 항목들을 차례로 꺼내서 반복하는 구조예요. 문법은 다음과 같아요
for 변수 in 리스트; do
명령들
done
예를 들어 1부터 5까지 숫자를 출력하는 스크립트는 이렇게 쓸 수 있어요
for i in 1 2 3 4 5
do
echo "숫자: $i"
done
리스트 부분에는 그냥 값들을 나열할 수도 있고, 글로벌 패턴을 써서 파일 목록을 가져올 수도 있어요. 예를 들어 for f in *.txt; do ... done 하면 현재 디렉토리의 .txt 파일들을 하나씩 변수 f에 넣어가며 반복할 거예요.
bash 확장 문법으로는 for (( 초기식; 조건식; 증감식 )) 형태의 C 스타일 for 문도 있지만, 초보 단계에서는 리스트를 사용하는 기본 for를 먼저 익히는 게 좋아요.
while 루프
while 문은 주어진 조건이 참인 동안 계속 반복해요. 형식은
while [ 조건식 ]; do
명령들
done
조건식을 평가해서 참이면 블록의 명령들을 실행하고, 다시 조건을 확인하는 식으로 동작해요. 예를 들어 5보다 작은 숫자를 증가시키면서 출력하는 코드
count=1
while [ $count -le 5 ]; do
echo "Count = $count"
count=$((count + 1))
done
위 스크립트는 count가 5 이하(-le는 less than or equal)인 동안 반복하고, 1씩 증가시키다가 6이 되면 루프를 끝내요.
또 다른 활용으로는, 파일의 내용을 한 줄씩 읽어 처리할 때도 while 루프를 많이 써요
while read line; do
echo "읽은 줄: $line"
done < somefile.txt
이렇게 하면 somefile.txt 파일을 끝까지 다 읽을 때까지 루프가 돈답니다.
until 루프
until 문은 while과 반대로 조건이 거짓인 동안 반복해요. 즉, 조건이 참이 되면 멈춰요. 구조는 while과 동일한데, 사용하는 키워드만 until로 다릅니다
until [ 조건식 ]; do
명령들
done
사실 until은 논리만 반대일 뿐 while로도 똑같이 쓸 수 있어서 자주 쓰이진 않지만, 경우에 따라 가독성을 높여줄 때가 있어요. 예를 들어, 어떤 작업이 성공할 때까지 반복하고 싶을 때
until ping -c1 example.com > /dev/null 2>&1; do
echo "네트워크 연결 대기 중..."
sleep 1
done
echo "네트워크에 연결되었습니다!"
위 스크립트는 ping이 성공해서 종료 상태가 0(참)이 될 때까지 계속 시도하다가, 성공하는 순간 루프를 탈출해요.
반복문 제어: 쉘 스크립트에서 break와 continue 키워드로 루프를 제어할 수 있어요. break를 만나면 현재 루프를 즉시 탈출하고, continue를 만나면 아래 명령을 건너뛰고 다음 반복을 시작해요. 중첩 루프에서 한 번에 여러 단계 탈출하는 건 좀 복잡한데, 일반적으로는 이러한 기본 제어문만 알아두면 돼요.
- 장점: 반복문 덕분에 같은 작업을 여러 번 수행하는 걸 자동화할 수 있어요. 파일 목록 처리, 일정 횟수 반복, 조건 충족까지 재시도 등 다양한 상황에서 활용되지요. 특히 for 루프는 리눅스의 여러 명령 출력과 결합하면 강력해요. 예를 들어 for user in $(cat users.txt)처럼 명령어 출력 결과를 리스트로 받아 처리할 수도 있어요. 이렇게 반복문을 쓰면 사람이 하던 반복 노가다 작업을 스크립트로 간단히 처리할 수 있다는 게 최대 장점이에요.
- 단점: 쉘의 반복문은 반복 횟수가 아주 많으면 속도가 느려질 수 있어요. 루프 안에서 외부 명령을 호출할 때마다 프로세스가 새로 생기니까, 수천 수만 번 돌리는 일에는 부적합해요. 또, 실수로 종료 조건을 잘못 쓰면 무한 루프에 빠질 위험이 있어요. 이 경우 Ctrl+C로 강제 종료해야 해서, 스크립트 짤 때 조건을 정확히 설정해야 해요. 마지막으로, 복잡한 중첩 루프 로직은 쉘 스크립트로 관리하기 어려울 수 있으니 아주 복잡한 반복이 필요하면 파이썬 같은 다른 스크립트 언어를 고려하는 게 좋아요.
쉘 스크립트 함수 정의 및 호출 방법
다른 프로그래밍 언어와 마찬가지로 쉘 스크립트에서도 함수(function)를 만들어서 코드를 구조화할 수 있어요. 함수는 자주 쓰이는 명령어 묶음을 만들어 놓고, 필요할 때마다 호출해서 쓰는 기능이에요.
함수 정의하기
쉘에서 함수를 정의하는 방법은 두 가지 형태가 있어요
함수이름() {
명령어들
}
또는
function 함수이름 {
명령어들
}
두 형태 모두 작동은 같아요. (뒤의 function 키워드는 bash에서 지원하는 키워드지만, 호환성을 위해 첫 번째 형태를 많이 씁니다.) 예를 들어 간단한 인사말 출력 함수를 만들어볼게요
greet() {
echo "안녕, $1님!"
}
이렇게 정의한 함수 greet는 $1 위치에 인자를 받아서 출력해요. 쉘 함수의 특성은, 함수 내부에서도 스크립트와 동일하게 $1, $2, ...로 인자 접근을 한다는 거예요. $0는 여전히 스크립트 이름이고, $1부터가 함수에 넘긴 첫 번째 인자가 되는 식이죠.
함수 호출하기
정의된 함수를 실행하려면 그냥 함수 이름을 명령처럼 호출하면 돼요. 위에서 정의한 greet 함수를 사용해볼까요
greet "홍길동"
호출 결과는 안녕, 홍길동님!이 출력될 거예요. 함수도 하나의 작은 쉘 스크립트 블록처럼 작동하니, 함수 안에서 return 명령으로 종료 상태 코드를 돌려줄 수도 있어요. 예를 들면 return 0은 성공을 의미하고, return 1 이상은 보통 오류를 의미하게 두죠. 이 값은 함수 호출 후 $?로 확인할 수 있어요.
쉘에서는 모든 변수가 기본적으로 전역 변수처럼 동작해서 함수 안에서도 외부 변수에 접근하고 변경할 수 있어요. 만약 함수 내부에서만 쓰는 임시 변수를 만들고 싶으면 local 키워드를 사용해서 지역 변수로 선언하면 돼요 (bash에서 지원).
- 장점: 함수를 쓰면 긴 스크립트를 구조화해서 관리하기 쉬워요. 반복되는 코드가 있을 때 함수로 만들어두면 재사용도 쉽고, 수정할 때도 한 군데만 고치면 되니까 유지보수가 편해져요. 또한 함수로 논리를 나누면 스크립트의 가독성이 올라가서, 어떤 부분이 어떤 일을 하는지 명확해져요.
- 단점: 쉘 함수는 다른 프로그래밍 언어의 함수보다 기능이 단순해요. 예를 들어 반환값은 오직 숫자 상태 코드 뿐이라서, 문자열이나 배열을 함수가 돌려주려면 글로벌 변수를 쓰거나 echo 출력 후 호출 쪽에서 캡처하는 식으로 우회해야 해요. 그리고 변수 스코프를 조심해야 하는데, 기본적으로 전역으로 동작하기 때문에 실수로 함수 안에서 전역 변수를 바꾸면 버그를 만들기 쉽죠. 마지막으로, 함수를 호출할 때도 결국 쉘에서는 스크립트의 일부분이 실행되는 거라서, 너무 복잡한 함수(특히 재귀 호출 같은 것)는 쉘에서는 잘 사용하지 않아요.
쉘 스크립트 특수 변수와 연산자
쉘에는 미리 약속된 특수 변수들이 있어요. 이것들은 달러 기호 $ 다음에 특정 기호나 숫자를 써서 접근할 수 있는데, 스크립트 실행 환경에 대한 유용한 정보를 담고 있어요
- $0: 현재 스크립트의 이름 (경로나 이름 전체). 함수 안에서는 스크립트 이름 그대로 유지돼요.
- $1, $2, ... $9: 스크립트에 넘겨준 인자 값들이 순서대로 담겨요. 예를 들어 myscript.sh hello world로 실행했다면 $1="hello", $2="world"가 돼요.
- $#: 스크립트에 넘겨진 인자의 개수를 의미해요. 위 예시에선 $#가 2겠죠.
- $@ / $*: 모든 인자를 한꺼번에 나타내요. 둘의 차이는 "${@}"와 "${*}"로 쓸 때 생기는데, $@는 각각의 인자를 따로따로 그대로 보여주고, $*는 한 덩어리 문자열로 보여준다는 점이에요. 간단히는 둘 다 전체 인자를 가리킨다고 이해하면 돼요.
- $?: 마지막으로 실행된 명령어의 종료 상태(exit status) 값을 담고 있어요. 보통 0이면 성공, 0이 아니면 뭔가 문제거나 조건 불만족인 경우가 많아요. (예: if 조건에서 많이 활용돼요. if command; then ... 이렇게 쓰면 command 실행 후 $?가 0인지 보고 참/거짓 결정하는 식이죠.)
- $$: 현재 쉘 프로세스의 PID(프로세스 ID) 번호예요. 스크립트마다 자기 프로세스 ID를 이 변수로 참조할 수 있어요.
- $!: 바로 직전에 백그라운드로 실행한 명령의 PID를 가리켜요. (command & 로 어떤 프로세스를 백그라운드로 돌렸다면 그 PID를 $!로 얻을 수 있어요.)
이런 특수 변수들은 읽기 전용이에요. 스크립트 작성 시 상황에 따라 유용하게 활용돼요. (예: 인자가 없으면 $#가 0일 테니 "사용법을 출력하고 종료" 같은 처리를 넣는다든지.)
쉘 스크립트에서 연산자와 관련해 알아둘 몇 가지도 있어요
- 산술 연산: 기본적으로 쉘은 숫자를 문자열처럼 다루지만, expr 명령이나 $(( )) 구문을 쓰면 덧셈, 뺄셈 등 산술 연산을 할 수 있어요. 예를 들어 result=$((5 + 3)) 하면 result 변수에 8이 들어가요. 또는 expr 5 + 3도 비슷한 역할을 하지만, $( )로 감싸서 사용해야 해서 요즘은 $(( ))를 더 많이 써요.
- 증감 연산: C 스타일로 (( i++ )), (( i+=1 )) 같은 표현도 가능해요 (bash 확장 기능).
- 문자열 연산: 문자열 비교는 =와 !=를 주로 써요 (대소문자 구별 비교). 그리고 문자열 길이 판단으로 -n (non-zero length), -z (zero length) 옵션을 [ ] 안에서 사용할 수 있어요. 예: [ -z "$str" ]는 빈 문자열이면 참.
- 파일 테스트 연산: 앞서 조건문에서 잠깐 언급했지만 -f (파일 존재?), -d (디렉토리 존재?), -r/-w/-x (읽기/쓰기/실행 권한) 등등 다양한 파일 관련 연산자가 있어요. 스크립트에서 파일 다룰 때 유용하죠.
- 논리 연산: [ 조건1 ] && [ 조건2 ]처럼 &&와 ||를 이용해 AND, OR 논리를 만들 수 있어요. (&&는 앞 조건이 참이어야 뒤를 실행, ||는 앞이 거짓일 때 뒤를 실행하는 식으로 동작해서, if 없이 조건문 역할을 간단히 대체하기도 해요.) 또한 test 명령 자체도 -a (AND), -o (OR) 연산자를 제공하지만, 잘 안 쓰이는 편이에요. 보통 [ 조건1 ] && [ 조건2 ] 형태가 많이 쓰여요.
쉘 스크립트 실행 방법과 팁
쉘 스크립트를 작성했다면, 이를 실행하는 방법은 두 가지가 있어요
- 쉘 인터프리터를 직접 실행: bash script.sh 또는 sh script.sh 처럼, 해당 쉘 프로그램에 스크립트 파일을 인자로 주는 방식이에요. 이 경우 스크립트 파일에 실행 권한이 없어도 실행돼요. 다만 어떤 쉘로 실행하느냐에 따라 문법 호환이 중요해요. (sh script.sh로 실행하면 일반적으로 시스템의 /bin/sh로 실행하는데, 그것이 bash일 수도 있고 아닐 수도 있어요. 그래서 특별히 bash 기능을 썼다면 bash script.sh로 실행하는 게 확실하겠죠.)
- 직접 실행 가능하게 만들기: 파일 첫 줄에 #!/bin/bash 같은 shebang을 써뒀다면, 한 번 chmod +x script.sh로 실행 권한을 준 후 ./script.sh처럼 바로 실행할 수 있어요. 이 방법이 편리하죠. 다만, 현재 디렉토리가 PATH에 없다면 ./를 꼭 붙여줘야 해요. (또는 PATH에 디렉토리를 추가해주거나, /usr/local/bin 같은 데에 스크립트를 두면 그냥 이름만으로 실행할 수도 있어요.)
source 또는 . 명령으로 스크립트를 실행하는 방법도 있는데, 이것은 해당 스크립트를 현재 쉘 환경에서 읽어서 실행해요. 예를 들어 source ~/.bashrc는 bash 설정 파일을 현재 쉘에 적용할 때 쓰죠. 일반적인 스크립트는 독립된 프로세스로 실행되는 게 보통이라서, source는 주로 환경 변수 설정 스크립트 등을 불러올 때 사용해요. (주의: 만약 스크립트 안에 exit이 있으면, source로 실행한 현재 쉘 자체가 종료될 수 있으니 조심!)
디버깅 팁: 쉘 스크립트는 에러가 나면 추적하기 어렵다고 했는데, 도움이 되는 옵션이 있어요. bash -x script.sh로 실행하면 실행되는 명령을 하나하나 출력하면서 진행돼서 어떤 부분에서 문제가 생겼는지 파악하기 좀 쉬워요. 또 set -x를 스크립트 내 적절한 위치에 넣으면 거기부터 디버그 모드가 시작돼요. 반대로 set +x로 끌 수도 있어요.
스크립트 작성 팁
- 스크립트 파일 이름은 가급적 기존 리눅스 명령어와 겹치지 않게 짓는 게 좋아요. 예를 들어 test라는 이름은 피해야 해요. 왜냐하면 쉘이 내부적으로 test라는 내장 명령을 가지고 있어서, 내 스크립트를 실행하려고 test라고 쳐도 그 내장 명령이 실행될 수 있거든요.
- 첫 줄 shebang 설정을 정확히 하면 나중에 어떤 환경에서도 해당 쉘로 실행되니 좋고요.
- 스크립트에 주석을 충분히 달아서, 나중에 봐도 이해하기 쉽게 해두는 습관도 중요해요. 쉘에서는 #으로 시작하는 부분이 한 줄 주석이 됩니다.
마지막으로, 새로운 스크립트를 만들었으면 작은 부분부터 테스트하면서 확장하는 게 좋아요. 처음부터 길게 만들기보다 한 단계씩 실행해보고 확인하면서 진행하면 오동작으로 시스템에 문제를 일으킬 위험도 줄어들고, 디버깅도 쉬워진답니다.
관련 공식 자료 및 영상
- GNU Bash 공식 페이지 – Bash 쉘에 대한 공식 정보와 매뉴얼
- Zsh 공식 웹사이트 – Z 쉘(Zsh)에 대한 정보와 문서
- 생활코딩: 리눅스 쉘 스크립트 소개 (YouTube) – 쉘 스크립트 기초를 영상으로 배우고 싶다면 도움이 되는 생활코딩 강좌
GNU Bash (GNU 프로젝트) | https://www.gnu.org/software/bash/ |
Zsh (Z Shell) 공식 웹사이트 | https://www.zsh.org/ |