CertNote/ 출퇴근 학습 노트
로드맵
KOEN
리눅스마스터 2급 · 2급리눅스마스터 2급
리눅스마스터 1급 · 1급리눅스마스터 1급
  • Week 1
    • 1.운영체제와 리눅스: 커널이 하는 일
    • 2.자유 소프트웨어와 배포판: 라이선스가 만든 생태계
    • 3.설치와 부팅: 전원을 켜는 순간부터
    • 4.파일시스템 구조와 파일의 종류: 모든 것은 파일이다
    • 5.Week 1 종합 복습: 개념을 꿰뚫는 시간
  • Week 2
  • Week 3
    • 1.셸의 정체: 명령을 해석하는 프로그램과 변수의 세계
    • 2.흐름을 잇고 비트는 기술: 리다이렉션, 파이프, 메타문자
    • 3.셸을 길들이다: 별칭, 히스토리, 그리고 작업 제어
    • 4.셸 스크립트: 명령을 코드로 엮는 첫걸음
    • 5.Week 3 종합 복습: 셸과 스크립트의 큰 그림
  • Week 4
  • Week 5
  • Week 6
  • Week 7
  • Week 8
  • Week 9
  • Week 10
  • Week 11
  • Week 12
  • Week 13
  • Week 14
합격 후기

© CertNote · 주간랩스

  • 소개
  • 수정 기록
  • 문의
  • 이용약관
  • 개인정보처리방침
← 리눅스마스터 1급/Week 3/Day 4

기준: 리눅스마스터 1급 출제 기준·오류 제보

리눅스마스터 1급· AssociateWeek 3 · Day 4읽기 약 10분
핵심 정리
  • shebang(#!/bin/bash)은 파일의 1행 1열에 와야 유효하다. ./script.sh는 자식 셸, source는 현재 셸에서 실행.
  • 위치 매개변수 — $0(이름) $1~$9(인자) $#(개수) "$@"(각각 분리) $?(종료 코드, 0=성공).
  • 조건 판단은 test = [ ]. 문자열은 =, 숫자는 -eq, 대괄호와 연산자 양옆에 공백 필수.
  • 분기는 if ... fi와 case ... esac(각 블록은 ;;, *)가 기본값). 반복은 for/while/until.
  • 함수의 return은 0~255 종료 코드만. 데이터는 echo + $(함수)로 넘기고, 내부 변수는 local로 격리한다.

Day 4 - 셸 스크립트: 명령을 코드로 엮는 첫걸음

shebang: 이 파일을 누가 해석할 것인가

매일 백업하고 로그를 정리하는 일을 매번 손으로 하면 사람은 지치고 실수한다. 셸 스크립트는 별도 컴파일 없이 셸이 한 줄씩 해석·실행하는 인터프리터 프로그램이다. 이 영역은 1급 실기 시험의 중심이다.

  • 스크립트 파일의 첫 줄에 오는 #!로 시작하는 줄을 shebang(셔뱅, 해시뱅)이라 부른다.
  • 이 파일을 어떤 인터프리터로 실행할지 커널에게 알려준다.
  • shebang이 없으면 현재 셸로 실행되어 다른 셸에서는 문법이 깨질 수 있다. 항상 명시하는 것이 좋다.
#!/bin/bash
# 위 줄이 shebang — 이 스크립트는 /bin/bash로 실행하라는 뜻
 
echo "Hello, Shell Script"
#!/bin/bash          # bash로 실행
#!/bin/sh            # POSIX sh로 실행
#!/usr/bin/env python3   # PATH에서 python3을 찾아 실행 (이식성↑)

스크립트를 실행하는 방법은 세 가지다.

chmod +x script.sh   # 실행 권한 부여
./script.sh          # ① 권한 부여 후 직접 실행 (shebang 사용)
 
bash script.sh       # ② bash에 인자로 넘겨 실행 (권한 불필요)
source script.sh     # ③ 현재 셸에서 실행 (변수가 현재 셸에 남음)

./script.sh(직접 실행)와 source script.sh(소싱)는 결정적으로 다르다. 직접 실행은 새 자식 셸을 띄워 그 안에서 돌리므로 스크립트 안에서 바꾼 변수가 끝나면 사라진다. 반면 source는 현재 셸 안에서 실행하므로 스크립트가 정의한 변수·함수가 그대로 남는다. 환경 설정 스크립트를 적용할 때 source를 쓰는 이유다(Day 1의 .bashrc source와 같은 원리).

⚠️ 함정: shebang의 #!는 반드시 파일의 맨 첫 줄, 맨 앞(1행 1열)에 와야 한다. 앞에 빈 줄이나 공백이 있으면 일반 주석으로 취급되어 무효가 된다. 또한 #! 뒤 인터프리터 경로는 절대 경로여야 한다.

변수와 위치 매개변수

스크립트 안의 변수는 Day 1에서 배운 셸 변수와 동일하다. 단, 대입할 때 = 양옆에 공백이 없어야 한다.

#!/bin/bash
NAME="linux"            # 올바름
COUNT=10
# NAME = "linux"        # 틀림! 공백이 있으면 NAME이라는 명령으로 해석됨
 
echo "$NAME 버전 $COUNT"

스크립트에 인자(argument)를 넘기면 위치 매개변수(positional parameter)로 받는다. 이것이 스크립트를 유연하게 만드는 핵심이다.

변수의미
$0스크립트 이름
$1 ~ $9첫 번째 ~ 아홉 번째 인자
${10}열 번째 이상 인자(중괄호 필수)
$#전달된 인자의 개수
$*모든 인자를 하나의 문자열로
$@모든 인자를 각각 분리해서
$?직전 명령의 종료 상태(0=성공)
$$현재 스크립트의 PID
$!마지막 백그라운드 작업의 PID
#!/bin/bash
echo "스크립트 이름: $0"
echo "첫 인자: $1, 둘째 인자: $2"
echo "인자 개수: $#"
echo "모든 인자: $@"
 
# 실행: ./test.sh apple banana
# 첫 인자: apple, 둘째 인자: banana / 인자 개수: 2

$*와 $@는 따옴표 없이 쓰면 동작이 같아 보이지만, 큰따옴표로 감쌌을 때 결정적으로 달라진다. "$*"는 모든 인자를 공백으로 이은 하나의 문자열("apple banana")이 되고, "$@"는 각 인자를 독립된 단어("apple" "banana")로 유지한다. 인자를 그대로 다른 명령에 넘길 때는 거의 항상 "$@"를 써야 공백 포함 인자가 깨지지 않는다.

$?(종료 상태)는 스크립트의 흐름 제어에 핵심이다. 모든 명령은 끝나면 0~255의 종료 코드를 남기는데, 0이 성공, 0이 아니면 실패다. 조건문은 이 종료 코드를 보고 분기한다. grep "root" /etc/passwd; echo $?처럼 직전 명령의 성패를 즉시 확인할 수 있다.

test와 [ ]: 조건을 판단하는 도구

조건문에서 "파일이 존재하는가", "숫자가 같은가", "문자열이 비었는가"를 판단하려면 test 명령 또는 그 별칭인 [ ]를 쓴다. 둘은 완전히 같다.

test -f /etc/passwd      # 파일이 존재하면 종료 코드 0
[ -f /etc/passwd ]       # 위와 완전히 동일
echo $?                  # 0 (참) 또는 1 (거짓)

파일 검사

연산자참이 되는 조건
-e file파일이 존재함(exists)
-f file일반 파일임
-d file디렉터리임
-r / -w / -x읽기/쓰기/실행 권한 있음
-s file크기가 0보다 큼(비어있지 않음)
-L file심볼릭 링크임

문자열 비교

연산자참이 되는 조건
-z str문자열 길이가 0(비었음)
-n str문자열 길이가 0이 아님
str1 = str2두 문자열이 같음
str1 != str2두 문자열이 다름

숫자 비교

연산자의미풀이
-eqequal같음
-nenot equal다름
-gtgreater than초과
-gegreater or equal이상
-ltless than미만
-leless or equal이하

⚠️ 함정: 문자열 비교는 =, !=를, 숫자 비교는 -eq, -ne를 쓴다. 이걸 섞으면 안 된다. [ "$a" = "$b" ]는 문자열로, [ "$a" -eq "$b" ]는 숫자로 비교한다. 또 하나 — [ ] 안에서는 대괄호 양옆과 모든 연산자 양옆에 반드시 공백이 있어야 한다. [$a=$b]나 [ $a=$b ]는 문법 오류다. 그리고 변수는 빈 값일 때 문법이 깨지지 않도록 "$a"처럼 큰따옴표로 감싸는 것이 안전하다.

bash에는 [ ]보다 강력한 [[ ]](이중 대괄호)도 있다. [[ ]]는 bash 전용 확장으로, 내부에서 &&·||·패턴 매칭(== 와일드카드)·정규표현식(=~)을 쓸 수 있고, 변수의 단어 분리·글로빙이 자동으로 억제되어 더 안전하다. 다만 POSIX sh 호환이 필요하면 [ ]를 써야 한다. 1급에서는 두 표기를 모두 알아 두는 것이 좋다.

조건문: if와 case

if-elif-else

#!/bin/bash
score=$1
 
if [ "$score" -ge 90 ]; then
    echo "A 등급"
elif [ "$score" -ge 80 ]; then
    echo "B 등급"
elif [ "$score" -ge 70 ]; then
    echo "C 등급"
else
    echo "재시험"
fi
  • 문법의 뼈대는 if 조건; then ... elif 조건; then ... else ... fi다.
  • fi는 if를 거꾸로 쓴 것으로 블록의 끝을 표시한다(같은 방식으로 case는 esac로 닫는다).
# 파일 존재 여부로 분기하는 실전 예
if [ -f /etc/nginx/nginx.conf ]; then
    echo "설정 파일이 있습니다"
else
    echo "설정 파일이 없습니다" >&2   # 에러는 stderr로
    exit 1
fi

if는 사실 [ ]만 받는 게 아니라 임의의 명령을 받아 그 종료 코드로 분기한다. if grep -q "root" /etc/passwd; then ...처럼 명령을 직접 조건으로 쓸 수 있다. 종료 코드 0이면 then, 0이 아니면 else로 간다. [ ] 자체가 test라는 명령이기에 가능한 일이다.

case: 여러 값을 패턴으로 분기

값이 여러 경우 중 하나일 때는 if-elif를 길게 늘어놓기보다 case가 깔끔하다.

#!/bin/bash
case "$1" in
    start)
        echo "서비스 시작"
        ;;
    stop)
        echo "서비스 중지"
        ;;
    restart|reload)         # | 로 여러 패턴 매칭
        echo "서비스 재시작"
        ;;
    *)                      # 그 외 모든 경우 (default)
        echo "사용법: $0 {start|stop|restart}"
        exit 1
        ;;
esac
  • 각 패턴 블록은 ;;로 끝나고, *)는 모든 나머지 경우를 받는 기본값이다.
  • case의 패턴에는 와일드카드(*, ?, [ ])를 쓸 수 있다.

리눅스 init 스크립트(/etc/init.d/의 서비스 제어 스크립트)는 거의 모두 위 형태의 case "$1" in start|stop|restart구조로 작성돼 있다. 그래서 service nginx start나 /etc/init.d/nginx restart 같은 명령이 동작한다. case 문은 이런 "명령어 디스패처"를 만드는 표준 패턴이다.

반복문: for와 while

for: 목록을 순회

#!/bin/bash
# 목록 순회
for fruit in apple banana cherry; do
    echo "과일: $fruit"
done
 
# 와일드카드로 파일 순회
for file in *.log; do
    echo "처리 중: $file"
    gzip "$file"
done
 
# 시퀀스 순회
for i in $(seq 1 5); do
    echo "반복 $i"
done
 
# C 스타일 (bash 확장)
for ((i=1; i<=5; i++)); do
    echo "카운트 $i"
done

while: 조건이 참인 동안 반복

#!/bin/bash
count=1
while [ "$count" -le 5 ]; do
    echo "횟수: $count"
    count=$((count + 1))    # 산술 확장으로 증가
done
 
# 파일을 한 줄씩 읽기 (매우 자주 쓰는 관용구)
while read line; do
    echo "줄: $line"
done < /etc/hosts

until은 while의 반대로, 조건이 거짓인 동안 반복하다 참이 되면 멈춘다.

count=1
until [ "$count" -gt 5 ]; do    # count가 5 초과가 될 때까지
    echo "$count"
    count=$((count + 1))
done

반복 제어 키워드도 알아 둔다.

키워드동작
break반복문을 즉시 빠져나감
continue현재 회차를 건너뛰고 다음 회차로
exit n스크립트 전체를 종료(종료 코드 n)

while read line; do ...; done < file에서 입력을 done 뒤에 <로 연결하는 위치를 헷갈리기 쉽다. 또한 cat file | while read line처럼 파이프로 while에 넣으면, 파이프의 오른쪽이 서브셸에서 돌아 루프 안에서 바꾼 변수가 루프 밖에 반영되지 않는다(서브셸 변수 격리). 그래서 파일을 읽을 때는 파이프 대신 done < file 리다이렉션을 쓰는 것이 안전하다.

함수: 코드를 재사용 단위로 묶기

반복되는 코드 블록은 함수(function)로 묶어 재사용한다.

#!/bin/bash
# 함수 정의 (두 가지 문법)
greet() {
    echo "안녕하세요, $1님"     # $1은 함수의 첫 인자
}
 
function backup() {            # function 키워드 방식 (bash)
    cp "$1" "$1.bak"
    return 0                    # 종료 코드 반환
}
 
# 함수 호출 — 괄호 없이 인자를 공백으로
greet "관리자"
backup /etc/hosts
echo "백업 결과 코드: $?"        # 함수의 return 값을 $?로 확인
  • 함수의 인자도 스크립트처럼 $1, $2, $#로 받는다.
  • 함수 안의 $1은 스크립트의 $1이 아니라 함수에 넘긴 인자다.

셸 함수는 값을 return으로 돌려줄 때 0~255의 종료 코드만 반환할 수 있다(일반 데이터를 반환하지 못함). 문자열이나 계산 결과를 돌려주려면 함수 안에서 echo로 출력하고 호출 측에서 명령 치환으로 받는다: result=$(my_func). return은 성공/실패 신호용, echo+$()는 데이터 전달용이라는 역할 분담을 기억해야 한다.

함수 안에서 선언한 변수도 기본적으로는 전역이라 스크립트 전체에 영향을 준다. 함수 안에서만 유효한 지역 변수를 만들려면 local var=값처럼 local 키워드를 붙인다. 큰 스크립트에서 함수가 의도치 않게 전역 변수를 덮어쓰는 버그를 막으려면 함수 내부 변수에 local을 쓰는 습관이 중요하다.

실기 대비의 핵심은 직접 작성하고 실행하며 오류를 만나 보는 것이다. 내일은 Week 3 전체를 종합 복습한다.

📖 용어

  • shebang(#!) : 이 파일을 어떤 인터프리터로 돌릴지 알려주는 첫 줄. 1행 1열이 아니면 무효다.
  • 위치 매개변수 : 스크립트나 함수에 넘긴 인자를 받는 $1, $2... 같은 변수들.
  • $# / $@ / $* : 인자 개수 / 인자를 각각 분리 / 인자를 한 문자열로. 넘겨줄 땐 "$@"가 안전하다.
  • 종료 코드 $? : 직전 명령이 남긴 0~255의 성패 신호. 0이 성공이고 조건문이 이걸 보고 분기한다.
  • test / [ ] : 조건을 판단해 종료 코드로 답하는 명령. 둘은 같은 것이며 양옆 공백이 필수다.
  • [[ ]] : bash 전용 강화판 조건식. &&·||·패턴 매칭·정규식(=~)을 쓸 수 있고 더 안전하다.
  • fi / esac : if와 case 블록을 닫는 키워드. 각각 if와 case를 거꾸로 쓴 것이다.
  • ;; : case의 각 패턴 블록을 끝맺는 표시. *)는 나머지 전부를 받는 기본값.
  • until : while의 반대. 조건이 거짓인 동안 돌다가 참이 되면 멈춘다.
  • break / continue : 반복문을 즉시 빠져나가기 / 이번 회차만 건너뛰기.
  • 서브셸 격리 : 파이프 오른쪽은 별도 셸에서 돌아 그 안의 변수 변경이 밖에 남지 않는 현상.
  • local : 함수 안에서만 유효한 지역 변수를 선언하는 키워드. 안 쓰면 전역이 된다.

📝 연습 문제

선택지를 클릭하면 정답·해설이 펼쳐집니다.

문제 1

셸 스크립트 첫 줄의 #!/bin/bash가 하는 역할은?

문제 2

스크립트에 전달된 인자의 개수를 담고 있는 위치 매개변수는?

문제 3

test 명령에서 두 숫자가 같은지 비교하는 연산자는?

문제 4

다음 case 문에서 모든 나머지 경우(default)를 처리하는 패턴은? ```bash case "$1" in start) echo "시작";; stop) echo "중지";; ???) echo "기타";; esac ```

문제 5

직전 명령의 성공 여부(종료 상태)를 담는 변수와, 그 "성공"을 뜻하는 값으로 옳은 것은?

문제 6

[ ](test) 사용 시 발생하는 문법 오류의 원인으로 옳은 것은?

문제 7

셸 함수에서 계산 결과 문자열을 호출한 쪽으로 전달하려 할 때 올바른 방법은?

이전셸을 길들이다: 별칭, 히스토리, 그리고 작업 제어Week 3 · Day 3다음 Week 3 종합 복습: 셸과 스크립트의 큰 그림Week 3 · Day 5

참고 공식 자료

  • 리눅스마스터 1급 공식 출제 기준(새 탭에서 열림)

이 페이지

  • shebang: 이 파일을 누가 해석할 것인가
  • 변수와 위치 매개변수
  • test와 [ ]: 조건을 판단하는 도구
  • 파일 검사
  • 문자열 비교
  • 숫자 비교
  • 조건문: if와 case
  • if-elif-else
  • case: 여러 값을 패턴으로 분기
  • 반복문: for와 while
  • for: 목록을 순회
  • while: 조건이 참인 동안 반복
  • 함수: 코드를 재사용 단위로 묶기
  • 용어
  • 연습 문제