- 셸은 커널과 사용자 사이의 명령 해석기다.
- 종류는 본 계열(
sh → ksh → bash → zsh)과 C 계열(csh → tcsh)로 나뉘고, bash가 리눅스 표준 로그인 셸이다.
- alias와 history를 처음 도입한 건 csh다.
- 셸 변수는 현재 셸에서만 유효하고 자식에게 상속되지 않으며,
export로 표시한 환경 변수는 자식 프로세스에게 상속된다. 상속은 부모→자식 한 방향뿐이다.
| 명령 | 보여주는 범위 |
|---|
set | 셸 변수 + 환경 변수 + 함수 (가장 넓음) |
env | 환경 변수만 |
export -p | export된 변수만 |
초기화 파일 읽기 순서는 셸의 종류에 따라 다르다.
- 로그인 셸:
/etc/profile → (~/.bash_profile → ~/.bash_login → ~/.profile 중 첫 번째 하나) → 로그아웃 시 ~/.bash_logout
- 비로그인 대화형 셸:
/etc/bashrc → ~/.bashrc
- 변경을 즉시 반영하려면
source 또는 .(점)을 쓴다. bash file은 새 자식 셸에서만 적용되므로 효과가 없다.
💡 개념: 핵심 한 줄 요약 — .bash_profile은 로그인 시 한 번, .bashrc는 셸 띄울 때마다. 그래서 alias·함수는 .bashrc에, 환경 변수는 .bash_profile에 두는 것이 관례다.
모든 프로세스는 세 개의 표준 통로를 갖는다 — stdin(0), stdout(1), stderr(2). 리다이렉션은 이 FD의 연결 대상을 바꾼다.
| 기호 | 의미 |
|---|
> / >> | stdout 덮어쓰기 / 이어쓰기 |
2> | stderr만 리다이렉션 |
&> 또는 > f 2>&1 | stdout+stderr 함께 |
< | 입력 받기 |
<< / <<< | 히어 도큐먼트 / 히어 스트링 |
2>&1의 순서가 결과를 가른다. 반드시 > file 2>&1(1을 먼저 파일로 돌린 뒤 2를 합침)이어야 둘 다 파일로 간다.
- 파이프(
|)는 앞 명령의 stdout을 뒤 명령의 stdin으로 잇는다(stderr는 기본적으로 안 넘어감).
- 필터(grep, sort, uniq, wc, cut, tee 등)를 조합해 복합 작업을 한 줄로 만든다.
- 와일드카드(글로빙)는 셸이 명령 실행 전에 파일 이름으로 확장한다:
*(0개 이상), ?(정확히 1개), [ ](괄호 중 하나). 정규표현식의 *(앞 문자 반복)와 의미가 다르다.
따옴표의 동작 차이는 시험에서 묻는 항목이다.
| 따옴표 | 변수 확장 | 명령 치환 | 와일드카드 |
|---|
' ' 작은 | ❌ | ❌ | ❌ |
" " 큰 | ✅ | ✅ | ❌ |
⚠️ 함정: sort file > file은 >가 명령 실행 전에 파일을 비우므로 내용이 사라진다. 같은 파일에 쓰려면 도구의 -o 옵션이나 임시 파일을 거쳐야 한다.
- alias는 긴 명령에 별명을 붙이며, 영구 적용하려면
~/.bashrc에 둔다. 임시 우회는 \명령, command 명령, 절대 경로.
- history는
!!(직전 전체), !n(n번), !$(직전 마지막 인자)로 재활용한다. HISTSIZE(메모리 개수), HISTCONTROL(중복·공백 제어)로 동작을 조절한다.
- 작업 제어가 이번 주에서 가장 실무적이다.
| 조작 | 동작 |
|---|
cmd & | 처음부터 백그라운드 실행 |
Ctrl-Z | 포그라운드 작업 일시 중지(suspend) |
Ctrl-C | 작업 종료(terminate) |
jobs | 작업 목록 |
bg %n / fg %n | 백그라운드 재개 / 포그라운드 복귀 |
nohup cmd & | SIGHUP 무시(터미널 닫아도 생존) + 백그라운드 |
disown %n | 실행 중 작업을 셸에서 분리 |
터미널이 닫히면 그 안의 작업에 SIGHUP이 전달되어 죽는다. nohup은 이 신호를 무시하게 하지만, 백그라운드 실행은 어디까지나 &가 담당하므로 둘을 함께 써야 한다.
- 스크립트 첫 줄의 shebang(
#!/bin/bash)이 인터프리터를 지정한다. ./script.sh는 자식 셸에서, source script.sh는 현재 셸에서 실행된다.
- 위치 매개변수:
$0(이름), $1~$9(인자), $#(개수), $@(분리된 전체), $?(종료 코드, 0=성공), $$(PID).
- 조건 판단(test/[ ]): 문자열은
=·!=·-z·-n, 숫자는 -eq·-ne·-gt·-lt, 파일은 -f·-d·-e. 대괄호 안과 연산자 양옆에 공백 필수.
- 제어 구조:
if-elif-else-fi, case-esac(패턴은 ;;로 끝, *)가 기본), for-do-done, while-do-done, until. break·continue로 반복 제어.
- 함수:
return은 0~255 종료 코드만, 데이터는 echo+$(함수)로 전달. 함수 내부 변수는 local로 지역화.
if는 [ ]뿐 아니라 임의의 명령을 조건으로 받아 그 종료 코드로 분기한다. if grep -q root /etc/passwd; then ...처럼. [ ]는 그 자체가 test라는 명령이기 때문에 if의 조건이 될 수 있다.
이번 주 개념들이 실제 스크립트에서 어떻게 함께 쓰이는지 정리한다.
#!/bin/bash
# 로그 디렉터리에서 오늘 .log 파일을 압축하고 결과를 기록
LOG_DIR="/var/log/myapp"
DATE=$(date +%Y%m%d) # 명령 치환 (Day3)
REPORT="/tmp/compress_$DATE.txt"
if [ ! -d "$LOG_DIR" ]; then # 파일 검사 + 조건문 (Day4)
echo "디렉터리 없음: $LOG_DIR" >&2 # stderr 리다이렉션 (Day2)
exit 1
fi
count=0
for file in "$LOG_DIR"/*.log; do # 와일드카드 + for (Day2,4)
[ -f "$file" ] || continue # 매칭 없을 때 건너뜀
gzip "$file" 2>> "$REPORT" # 에러만 이어쓰기 (Day2)
count=$((count + 1)) # 산술 확장 (Day3)
done
echo "압축 완료: $count개" | tee -a "$REPORT" # 파이프+tee (Day2)
- 이 짧은 스크립트 안에 명령 치환, 조건문, 파일 검사, 리다이렉션, 와일드카드, 반복문, 산술 확장, 파이프, tee가 모두 들어 있다.
- 이번 주에 배운 것이 따로 노는 지식이 아니라 하나의 작업을 자동화하기 위해 맞물리는 부품임을 보여준다.
이런 "로그 압축·정리" 스크립트는 실제로 logrotate라는 표준 도구가 대신해 준다. 하지만 logrotate 내부 동작과 커스텀 정리 로직은 결국 이런 셸 스크립트의 원리 위에 서 있다. 기본기를 알면 표준 도구가 무엇을 하는지도 이해된다.
- 셸 계열 분류: ksh는 본 계열(이름에 속지 말 것), csh가 alias·history 도입
$SHELL은 로그인 기본 셸, 현재 셸은 echo $0
- 셸 변수↔환경 변수 차이는 export 여부, 상속은 부모→자식 단방향
- 로그인 셸:
/etc/profile → ~/.bash_profile, 대화형: ~/.bashrc
- FD: stdin 0, stdout 1, stderr 2 /
2>&1은 순서가 중요
- 글로빙
*≠정규표현식 *
- 작은따옴표=전부 차단, 큰따옴표=변수만 확장
Ctrl-Z=멈춤, Ctrl-C=종료 / nohup+& 함께
- test: 문자열
=, 숫자 -eq, 공백 필수
$?=종료 코드(0=성공), 함수 return은 종료 코드만
- 본 계열 / C 계열 :
sh에서 이어진 bash·ksh·zsh 무리와, csh에서 갈라진 tcsh 무리.
- 셸 변수 vs 환경 변수 :
export 하지 않아 자식에게 안 넘어가는 변수와, 넘어가는 변수.
~/.bash_profile vs ~/.bashrc : 로그인할 때 한 번 읽히는 파일과, 셸을 새로 띄울 때마다 읽히는 파일.
source(.) : 파일을 현재 셸 안에서 읽어 설정을 즉시 반영하는 명령.
- FD 0/1/2 : 표준 입력·표준 출력·표준 에러의 번호. 리다이렉션은 이 번호의 연결 대상을 바꾼다.
2>&1 : 에러를 출력 쪽으로 합치는 표기. > 파일 뒤에 써야 둘 다 파일로 간다.
- 글로빙 : 셸이
*·?·[]를 실제 파일명으로 미리 바꾸는 확장. 정규식과 의미가 다르다.
- SIGHUP : 터미널이 닫힐 때 그 안의 작업에 전달되는 신호.
nohup으로 무시시킨다.
- 작업 번호(
%n) : 현재 셸 안에서만 통하는 작업 식별 번호. fg %1처럼 쓴다.
- shebang : 스크립트를 어떤 인터프리터로 실행할지 지정하는 첫 줄.
$? : 직전 명령의 종료 코드. 0이 성공이고 조건문이 이 값을 보고 분기한다.
test/[ ] : 조건을 판단해 종료 코드로 답하는 명령. 문자열은 =, 숫자는 -eq, 공백 필수.
local : 함수 안에서만 살아 있는 지역 변수를 선언하는 키워드.
📝 연습 문제
선택지를 클릭하면 정답·해설이 펼쳐집니다.
다음 셸 중 본(Bourne) 셸 계열이 아닌 것은?
현재 실행 중인 셸의 이름을 확인하는 가장 정확한 방법은?
command > out.txt 2>&1 과 command 2>&1 > out.txt 의 차이로 옳은 것은?
와일드카드(글로빙)에 대한 설명으로 옳은 것은?
다음 중 변수가 확장되어 출력되는 것은? (NAME=linux인 상태)
Ctrl-Z로 일시 중지한 작업을 백그라운드에서 다시 실행시키는 명령은?
터미널을 닫아도 작업이 종료되지 않게 하려고 한다. 가장 적절한 실행 명령은?
test 명령에서 /etc/passwd가 디렉터리인지 검사하는 연산자는?
$? 변수에 대한 설명으로 옳은 것은?
셸 변수를 자식 프로세스에게 상속시키는 명령과, 변경한 .bashrc를 현재 셸에 즉시 반영하는 명령을 올바르게 짝지은 것은?
case 문에서 여러 패턴을 하나의 블록으로 처리하고, 각 블록을 종료하는 기호를 올바르게 짝지은 것은?
다음 스크립트의 실행 결과로 옳은 것은?
```bash
#!/bin/bash
count=0
for f in a b c; do
count=$((count + 1))
done
echo $count
```