환경 변수(environment variable)는 셸과, 셸이 실행하는 프로그램이 함께 보는 이름=값 쌍입니다. env·set·export로 보고 바꾸며, 명령을 찾을 디렉터리를 적은 PATH가 대표입니다. 셸 스크립트(shell script)는 셸 명령을 파일에 적어 차례로 실행하게 한 것으로, 변수·조건·반복을 씁니다.
리눅스 기본 명령어에서 ls라고만 쳤는데, 셸은 어디에 있는 ls를 실행했을까요?
셸은 PATH라는 환경 변수에 적힌 디렉터리를 뒤져 찾습니다.
같은 명령을 매번 치는 대신 파일에 적어 두면 셸 스크립트가 되고, 실행 권한은 파일 권한과 링크에서 본 chmod로 줍니다.
아래 그림은 변수가 자식 프로세스로 전해지는 모습과, 셸이 PATH의 디렉터리를 차례로 뒤져 실행 파일을 찾는 과정을 보입니다.
1셸 변수와 환경 변수 — env·set·export
셸에서 이름=값으로 만든 변수는 셸 변수입니다. 그 셸 안에서만 쓰이고, 셸이 실행하는 프로그램(자식 프로세스)에는 전해지지 않습니다. export를 하면 환경 변수가 되어 자식 프로세스에도 전해집니다. = 앞뒤에는 빈칸을 두지 않고, 값을 꺼낼 때는 $HOME처럼 이름 앞에 $를 붙입니다.
| 명령 | 하는 일 |
|---|---|
env | 환경 변수만 늘어놓는다 |
set | 셸 변수까지 모두 늘어놓는다 |
export 이름=값 | 변수를 환경 변수로 만든다 |
set(Windows 명령 프롬프트) | 환경 변수를 보고 정한다. 값은 %PATH%처럼 꺼낸다 |
2PATH — 명령을 찾는 차례
PATH는 명령을 찾을 디렉터리를 콜론(:)으로 이어 적은 환경 변수입니다(예: /usr/local/bin:/usr/bin:/bin). 명령 이름만 치면 셸은 PATH의 디렉터리를 앞에서부터 차례로 뒤져, 처음 찾은 실행 파일을 실행합니다. 같은 이름이 두 곳에 있으면 앞쪽 것이 실행되고, 끝까지 없으면 command not found를 알립니다.
디렉터리를 더할 때는 export PATH=$PATH:$HOME/bin처럼 원래 값 뒤에 이어 붙입니다. 현재 디렉터리는 보통 PATH에 없어서, 여기 있는 스크립트는 ./sum.sh처럼 경로를 붙여 실행합니다.
3셸 스크립트 — 셸의 종류와 문법
스크립트의 첫 줄 #!/bin/bash는 어느 셸로 실행할지 적은 것입니다. 셸은 여럿이고, 시험에는 넷이 나옵니다.
| 셸 | 가르는 특징 |
|---|---|
| sh | 본 셸(Bourne shell). UNIX의 표준 셸로, bash·ksh의 바탕 |
| bash | 본 어게인 셸(Bourne-Again shell). sh와 호환되고, 대부분의 Linux에서 기본 셸 |
| csh | C 셸(C shell). C 언어와 닮은 문법 |
| ksh | 콘 셸(Korn shell). sh와 호환되며 csh의 기능을 더함 |
스크립트에 쓰는 기본 문법은 다섯입니다.
| 문법 | 꼴 | 뜻 |
|---|---|---|
| 변수 | n=1 · $n | 만들기 · 꺼내기 |
| 출력 | echo "n은 $n" | 화면에 한 줄 찍는다. 큰따옴표 안의 $n은 값으로 바뀐다 |
| 계산 | $((n + 1)) | 식의 값. %는 나머지 |
| 조건 | if [ $n -eq 1 ] … fi | -eq 같다 · -ne 다르다 · -gt 크다 · -lt 작다 · -ge 크거나 같다 · -le 작거나 같다 |
| 반복 | for n in 1 2 3 … done · while [ 조건 ] … done | 목록의 값을 하나씩 · 조건이 참인 동안 |
예제
먼저 그림의 변수 둘을 만들고, bash -c로 새 셸(자식 프로세스)을 띄워 두 변수를 찍게 합니다.
A=사과
export B=배
bash -c 'echo "A=$A, B=$B"'
A=, B=배
작은따옴표 안의 $A·$B는 처음 셸이 풀지 않고 자식 셸이 받아서 풉니다. export한 B만 자식 셸에 전해졌고, A는 처음 셸 안에만 있습니다.
다음은 sum.sh에 적어 chmod +x sum.sh로 실행 권한을 준 뒤 ./sum.sh로 실행한 스크립트입니다.
#!/bin/bash
sum=0
for n in 1 2 3 4
do
if [ $((n % 2)) -eq 0 ]
then
sum=$((sum + n))
fi
done
echo "합계: $sum"
합계: 6
n을 2로 나눈 나머지가 0인 짝수만 더합니다.
| n | n % 2 | sum |
|---|---|---|
| 1 | 1 | 0 |
| 2 | 0 | 2 |
| 3 | 1 | 2 |
| 4 | 0 | 6 |
시험 포인트
- 필기:
env는 환경 변수만,set은 셸 변수까지 모두 보입니다.export는 셸 변수를 환경 변수로 만들어 자식 프로세스에 전합니다. Windows 명령 프롬프트에서 환경 변수를 보고 정하는 명령은set입니다. PATH는 앞 디렉터리부터 뒤져 처음 찾은 것을 실행합니다. 셸 넷(sh·bash·csh·ksh)을 특징과 짝짓는 문제도 나옵니다. - 실기: 셸 스크립트의 출력을 줄바꿈까지 그대로 씁니다. 명령을 묻는 문제에는
env·set·export를 씁니다.
확인 문제
1. PATH가 /opt/tools:/usr/bin이고, 실행 파일 report가 /usr/bin과 /opt/tools 두 곳에 있습니다. report라고만 치면 일어나는 일로 알맞은 것을 고르세요.
- ①
/opt/tools/report가 실행됩니다. - ②
/usr/bin/report가 실행됩니다. - ③ 두 파일이 차례로 실행됩니다.
- ④ 이름이 겹쳐
command not found가 납니다.
정답
①입니다. 셸은 PATH에 적힌 차례대로 뒤지고, 처음 찾은 곳에서 멈춥니다. /opt/tools가 앞에 있습니다.
- ②
/usr/bin이 시스템 디렉터리라고 먼저 뒤지지 않습니다. 차례는 PATH에 적힌 대로입니다. - ③ 처음 찾은 파일 하나만 실행하고 뒤는 보지 않습니다.
- ④ 겹쳐도 오류가 아닙니다.
command not found는 어디에서도 못 찾았을 때 납니다.
2. 셸에서 MODE=test를 친 뒤 ./run.sh를 실행했더니, run.sh 안의 echo $MODE가 빈 줄을 찍었습니다. 까닭으로 알맞은 것을 고르세요.
- ①
MODE는 셸 변수라 자식 프로세스인run.sh에 전해지지 않았습니다. - ②
=앞뒤에 빈칸이 없어서 값이 들어가지 않았습니다. - ③
run.sh에 실행 권한이 없었습니다. - ④ PATH에
MODE가 없었습니다.
정답
①입니다. export MODE=test로 만들었어야 run.sh가 값을 받습니다.
- ② 거꾸로입니다.
=앞뒤에 빈칸을 두면 셸이MODE를 명령 이름으로 읽습니다. - ③ 실행 권한이 없으면
run.sh가 돌지 않아echo가 찍히지도 않습니다. - ④ PATH는 명령을 찾을 디렉터리 목록입니다. 변수를 건네는 일과 상관없습니다.
3. 다음 일을 하는 명령을 쓰세요. 답을 끝까지 쓴 뒤 정답을 펼치세요.
- (가) 이 셸이 실행하는 프로그램에 건너가는 변수만
이름=값으로 늘어놓습니다. - (나) 이 셸 안에서만 쓰려고 만든 변수까지 모두 늘어놓습니다.
- (다) 셸 변수
DB_HOST를 이 셸이 실행하는 프로그램에서도 읽게 합니다.
정답
받는 답: (가) env · (나) set · (다) export DB_HOST
(가)의 실마리는 '실행하는 프로그램에 건너가는 변수만', (나)는 '셸 안에서만 쓰는 변수까지 모두', (다)는 '실행하는 프로그램에서도 읽게'입니다.
- 틀리는 꼴
set— (가)에: 셸 변수까지 모두 늘어놓아 건너가지 않는 변수도 섞입니다. - 틀리는 꼴
env— (나)에: 환경 변수만 보입니다. export하지 않은 셸 변수는 빠집니다. - 틀리는 꼴
set DB_HOST— (다)에: Linux 셸의set은 변수를 건네지 않습니다. Windows의set과 헷갈린 꼴입니다.
4. 예제 sum.sh의 조건을 -ne 0으로 바꿨습니다. 출력을 그대로 쓰세요. 답을 끝까지 쓴 뒤 정답을 펼치세요.
#!/bin/bash
sum=0
for n in 1 2 3 4
do
if [ $((n % 2)) -ne 0 ]
then
sum=$((sum + n))
fi
done
echo "합계: $sum"
정답
받는 답: 합계: 4
-ne는 '다르다'라서, 나머지가 0이 아닌 홀수 1과 3만 더합니다.
- 틀리는 꼴
합계: 6:-ne를-eq처럼 읽은 꼴입니다. - 틀리는 꼴
합계: 10: 조건을 건너뛰고 넷을 다 더한 꼴입니다.
5. 다음 스크립트의 출력을 그대로 쓰세요. 답을 끝까지 쓴 뒤 정답을 펼치세요.
#!/bin/bash
i=1
while [ $i -le 3 ]
do
echo "회차 $i"
i=$((i + 1))
done
echo "끝 $i"
정답
받는 답:
회차 1
회차 2
회차 3
끝 4
-le는 '작거나 같다'라서 i가 1·2·3일 때 세 번 돕니다. 세 번째에 i가 4가 되면 조건이 거짓이 되어 반복을 나오고, 마지막 줄이 4를 찍습니다.
- 틀리는 꼴
끝 3: 반복을 나올 때 i가 이미 4로 늘어난 것을 놓친 꼴입니다. - 틀리는 꼴
회차 1~회차 2까지만:-le를-lt(작다)로 읽은 꼴입니다.