 |
|
난이도 : 중급 Kurt A. Riley, IT 컨설턴트
옮긴이: 박재호 이해영 dwkorea@kr.ibm.com
2008 년 10 월 28 일 유닉스(UNIX®) 사용자라면 콘(Korn) 셸 스크립트 활용법을 알아두어야 합니다. 셸 스크립트를 이용하면 많은 작업을 자동화할 수 있으므로 상당한 시간이 절약됩니다. 처음에는 어려워 보일지도 모르지만, 좋은 문서를 읽고 조금만 연습하면 금방 익숙해집니다. 이 기사에서는 콘 셸 스크립트를 작성하는 방법을 소개합니다.
셸이란?
IBM® AIX® 운영체제와 기타 유닉스 계열의 운영체제는 커널과 통신하는 방법이 필요한데, 이러한 목적을 셸(shell)이 수행한다. 현재 사용할 수 있는 셸 종류는 여러 가지이지만, 이 기사에서는 콘(Korn) 셸에 초점을 맞춘다. AIX는 기본적으로 콘 셸을 사용한다.
AIX로 로그인하면 특정 디렉터리에서 프롬프트가 뜬다. 대개는 사용자 홈 디렉터리가 기본 디렉터리다. 홈 디렉터리라고 부르는 이유는 사용자 디렉터리가 다음에서 보듯이, 흔히 디렉터리 구조가 /home/ 아래 위치하기 때문이다.
시스템에 로그인하면 명령행에 명령 프롬프트가 뜬다. 여기서 유닉스 명령을 입력하여 실행한다. 사용자는 명령행에서 다양한 셸 명령을 실행하여 유닉스 커널과 통신하는데, 목적에 따라, 날짜를 점검하는 한 줄짜리 명령에서 여러 줄짜리 명령까지 길이는 다양하다. Listing 1은 몇 가지 명령을 보여준다.
Listing 1. 예제 명령
$date
Fri May 1 22:59:28 EDT 2008
$uptime
10:59PM up 259 days, 9:44, 5 users, load average: 3.81, 14.27, 13.71
$hostname
gonzo
|
스크립트라는 파일에 여러 셸 명령을 조합한 후 한 번에 실행하는 멋진 방법도 있다. 같은 명령을 여러 차례 반복해야 한다면 셸 스크립트가 편리하다. 스크립트 파일을 한 번만 작성하면 명령행에서 같은 명령을 재차 입력할 필요가 없다.
첫 번째 콘 셸 스크립트 작성하기
콘 셸 스크립트에서 첫 행은 셸 자체다. 다음과 같이 표기한다.
AIX에서 콘 셸 스크립트를 작성하려면 편집기가 필요하다. 가장 많이 사용하며 가장 구하기 쉬운 편집기가 vi다. 처음에는 다소 까다롭지만, 사용할수록 편리해지는 편집기다. vi 편집기 사용법을 설명하는 좋은 참고 서적이 많다.
첫 번째 콘 셸 스크립트를 작성하려면 먼저 편집기를 연 후 첫 행에 셸 이름을 추가한다. 그런 다음에 스크립트 작성자, 스크립트 기능, 스크립트 작성 날짜 등 스크립트 관련 정보를 추가한다. 스크립트 이름은 마음대로 정해도 좋다. 단, 확장자는 콘 셸 스크립트를 나타내는 .ksh로 붙이는 편이 좋다. 확장자를 반드시 .ksh로 정해야 한다는 규칙은 없지만 좋은 습관이므로 가능하면 따른다. 주석은 # 기호로 시작한다. Listing 2를 살펴보자.
Listing 2. 스크립트 머리글 예제
$vi my_first_script.ksh
#!/bin/ksh
###################################################
# 작성자: Jason Thomas
# 목적: 이 스크립트는 첫 스크립트를 개발하는 방법을 보여주기 위해 작성했다.
# 2008년 5월 1일
###################################################
|
위 스크립트 머리글은 아주 단순하지만 중요한 정보를 모두 제공한다.
변수
스크립트에서 변수를 정의하는 방법은 매우 간단하다. 보통 변수 이름을 모두 대문자로 쓰지만, 반드시 그래야 한다는 규칙은 없다. Listing 3을 살펴보자.
Listing 3. 변수 예제
# 변수 정의
HOME="/home/jthomas" # 간단한 홈 디렉터리
DATE=$(date) # 콘 셸 명령인 date를 수행한 결과를 DATE에 저장한다.
HOSTNAME=$(hostname) # HOSTNAME은 hostname 명령을 수행한 결과를 저장한다.
PASSWORD_FILE="/etc/passwd" # AIX password 파일 경로
|
콘 셸 스크립트 짜기
지금까지 콘 셸 스크립트를 시작하는 방법, 기본적인 스크립트 머리글을 작성하는 방법, 변수를 정의하는 방법을 살펴보았다. 이제 콘 셸 코드를 살펴볼 차례다.
먼저 파일에서 행을 읽는 방법이다. 여기서는 앞서 변수로 정의한 /etc/passwd 파일에서 각 행을 읽어 사용자 이름만 뽑아 출력한다. 자세한 코드는 Listing 4를 참조한다.
Listing 4. for 루프
$vi my_first_script.ksh
#!/bin/ksh
###################################################
# 작성자: Jason Thomas
# 목적: 이 스크립트는 첫 스크립트를 개발하는 방법을 보여주기 위해 작성했다.
# 2008년 5월 1일
###################################################
# 변수 정의
HOME="/home/jthomas" # 간단한 홈 디렉터리
DATE=$(date) # 콘 셸 명령인 date를 수행한 결과를 DATE에 저장한다.
HOSTNAME=$(hostname) # HOSTNAME은 hostname 명령을 수행한 결과를 저장한다.
PASSWORD_FILE="/etc/passwd" # AIX password 파일 경로
# 코드 시작
for username in $(cat $PASSWORD_FILE | cut -f1 -d:)
do
print $username
done
|
위 코드는 for 루프를 사용한다. /etc/passwd 파일을 연 후 한 번에 한 행씩 읽어 첫 번째 필드를 추출하여 출력한다. 특수 문자 |에 주목한다. 파이프라고 하는데, 파이프는 한 명령의 출력을 다른 명령의 입력으로 재지정한다.
스크립트를 저장한 후 실행하면 Listing 5와 같은 결과를 얻는다.
Listing 5. 스크립트 실행하기
$./my_first_script.ksh
root
daemon
bin
sys
adm
uucp
nobody
lpd
|
위 스크립트를 실행하면 결과는 화면에 출력된다. 다음과 같이 결과를 파일에 출력하는 방법도 있다.
print $username >> /tmp/usernames |
>> 기호는 출력을 /tmp/usernames 파일에 계속 덧붙이라는 뜻이다. 위 명령을 수행하면 화면에는 아무런 결과가 표시되지 않는다. 결과를 화면에도 출력하고 파일로도 보내려면 다음 명령을 사용한다.
print $username | tee -a /tmp/usernames |
tee 명령은 결과를 화면과 파일로 동시에 보낸다.
지금까지 for 루프를 사용하여 파일에서 각 행을 읽는 방법, 각 행에서 사용자 이름만 추출하는 방법, 결과를 화면이나 파일로 보내는 방법을 익혔다.
오류 검사
시작부터 /etc/passwd 파일이 존재하지 않는다면 어떤 일이 벌어질까? 결론만 말하면 스크립트는 실패한다. Listing 6은 파일 존재 여부를 확인하는 코드다.
Listing 6. 파일 존재 여부를 확인하는 코드
# 코드 시작
if [[ -e $PASSWORD_FILE ]]; then # 파일이 존재하며 계속할 수 있는지를 점검한다.
for username in $(cat $PASSWORD_FILE | cut -f1 -d:)
do
print $username
done
else
print "$PASSWORD_FILE was not found"
exit
fi
|
위 코드는 if 조건문을 사용한다. /etc/passwd 파일이 존재하면 스크립트를 계속 실행한다. 파일이 존재하지 않으면 "/etc/passwd file was not found"라는 오류 메시지를 화면에 출력한 후 스크립트를 종료한다. 조건부 if 문은 if로 시작해 (if를 뒤집은) fi로 끝난다.
달러 물음표 기호($?)
AIX에서 명령을 실행할 때마다 시스템은 달러 물음표($?)라는 변수를 설정한다. 구체적으로 명령이 성공하면 0, 명령이 실패하면 0이 아닌 값으로 설정한다. 콘 셸 스크립트를 짤 때는 이 값이 매우 유용하다. Listing 7은 올바른 AIX 명령을 실행한 후와 잘못된 AIX 명령을 실행한 후 $? 변수 값을 보여준다.
Listing 7. 올바른 AIX 명령을 실행한 후와 잘못된 AIX 명령을 실행한 후 $? 변수 값
$date
Sat May 10 00:02:31 EDT 2008
$echo $?
0
$uptime
12:02AM up 259 days, 10:47, 5 users, load average: 4.71, 10.44, 12.62
$echo $?
0
$IBM
ksh: IBM: not found.
$echo $?
127
$aix
ksh: aix: not found.
$echo $?
127
$ls -l /etc/password
ls: 0653-341 The file /etc/password does not exist.
$echo $?
2
|
콘 셸 스크립트를 작성할 때 오류를 쉽게 확인하는 다른 방법도 제공한다. 다음은 /etc/passwd 파일이 존재하는지 확인하는 다른 방법이다.
#Begin Code
PASSWORD_FILE="/etc/passwd"
ls -l $PASSWORD_FILE > /dev/null 2>&1 |
ls 명령은 파일을 열거한다. 하지만 명령이 출력하는 결과에는 관심이 없다. 명령을 수행한 결과가 성공인지 실패인지가 중요하다. > 기호는 명령이 출력하는 결과를 재지정한다. 출력을 재지정하는 방법은 이 기사 후반에 다룬다.
Listing 8은 스크립트에서 $? 변수를 사용하는 방법을 보여준다.
Listing 8. 스크립트에서 $? 변수 사용하기
#Begin Code
PASSWORD_FILE="/etc/passwd"
ls -l $PASSWORD_FILE > /dev/null 2>&1
if [[ $? != 0 ]]; then
print "PASSWORD_FILE was not found"
exit
else
for username in $(cat $PASSWORD_FILE | cut -f1 -d:)
do
print $username
done
fi
|
파일이 실제로 존재하는지 확인하는 대신 ls 명령으로 파일을 열거한다. 파일을 열거할 수 있다면 파일이 존재한다는 뜻이다. 파일을 열거할 수 없다면 파일이 존재하지 않는다는 뜻이다. AIX에서 파일을 열거하려면 ls -l 파일 이름 명령을 사용한다. 그런 다음, $? 변수 값을 살펴 AIX 명령이 성공했는지 여부를 확인한다.
표준 입력, 표준 출력, 표준 오류
표준 입력, 표준 출력, 표준 오류는 반드시 이해하고 넘어갈 개념이다. 기본적으로 입출력은 세 가지로 나뉜다. AIX에서는 이를 STDIN, STDOUT, STDERR이라고 부른다. STDIN은 키보드 같은 입력을 가리킨다. STDOUT은 화면 등의 출력을 가리킨다. STDERR은 명령이 실패할 때 오류를 출력하는 위치다. STDIN, STDOUT, STDERR 파일 기술자는 각각 0, 1, 2로 사상된다.
Listing 9는 명령이 성공인지 실패인지 확인하는 코드다.
Listing 9. STDOUT과 STDERR로 출력 재지정하기
$date /dev/null 2>&1 # 이 명령 출력 결과는 결코 화면에 보이지 않는다.
if [[ $? = 0 ]]; then
print "The date command was successful"
else
print "The date command failed
fi
|
위 코드는 AIX date 명령을 실행한다. 위 코드에서 STDOUT(파일 기술자 1)이나 STDERR(파일 기술자 2)로 보내지는 출력은 화면에 표시되지 않는다. 조건부 if 문은 명령 반환값을 확인하는데 앞서 설명했듯이 0은 성공을 뜻하고, 0이 아닌 값은 실패를 뜻한다.
함수
콘 셸 스크립트에서 function은 예약된 단어다. 함수는 스크립트를 여러 세그먼트로 나누는 방식 중 하나다. 함수를 호출하면 해당 세크먼트만 실행된다. 지금까지 작성한 코드를 바탕으로 오류 점검 함수를 구현해보자. 자세한 코드는 Listing 10을 참조한다.
Listing 10. 오류 점검 함수
##################
function if_error
##################
{
if [[ $? -ne 0 ]]; then # 함수에 전달하는 오류 코드를 점검한다.
print "$1" # if rc > 0이면 오류 메시지를 출력하고 끝낸다.
exit $?
fi
}
|
스크립트에서 명령을 실행할 때마다 위와 비슷한 코드를 중복하여 $?로 반환값을 확인하는 방법도 있다. 하지만 if_error 함수를 호출하는 편이 더 간결하고 편하다. 예제는 Listing 11을 참조한다.
Listing 11. if_error 함수 호출하기
rm -f /tmp/file # 파일 삭제
if_error "Error: Failed removing file /tmp/file"
mkdir /tmp/test # 디렉터리 test 생성
if_error "Error: Failed trying to create directory /tmp/test"
|
위 코드는 명령을 실행할 때마다 if_error 함수를 호출한다. if_error 함수는 오류 발생 시 화면으로 출력할 메시지를 인수로 받는다. 이렇듯 함수를 정의하면 스크립트 코드를 한 번만 작성한 후 반복해서 두고두고 사용할 수 있다. 따라서 스크립트를 짜기가 쉬워지고 빨라진다.
case 문
case 문은 if와 비슷한 조건문이다. case 문은 case로 시작해 (case를 뒤집은) esac로 끝난다. case 문을 사용하면 스크립트에 새로운 조건을 추가하기 쉬워진다. Listing 12는 case 문을 사용하는 예제다.
Listing 12. case 문
case value in
"내 패턴") 수행할 명령
값이 "내 패턴"과 일치할 때
;;
esac
|
예를 들어, 하루 중 특정한 시각마다 파일을 삭제하려고 한다. 먼저 현재 시각을 계산하는 변수를 생성한다.
Listing 13에 실린 코드는 파일을 10시와 11시에 지운다. 즉, 스크립트를 실행할 때마다 $TIME 변수 값을 확인하여 case 문에 일치하는 조건이 있는지 찾는다. 조건에 맞는 해당 코드를 실행한다.
Listing 13. 시간을 확인하는 case 문
case $TIME in
"2200") #This means 10:00
rm -f /tmp/file1
;;
"2300")#This means 11:00
rm -f /tmp/file1
;;
"*")
echo "Do nothing" > /dev/null
;;
esac
|
전체 스크립트 합치기
지금까지 스크립트 머리글을 작성하고, 변수 몇 개를 정의하고, 함수 하나를 추가했다. 전체 코드는 Listing 14를 참조한다.
Listing 14. 예제 콘 셸 스크립트
$vi my_second_script.ksh
#!/bin/ksh
###################################################
# 작성자: Jason Thomas
# 목적: 이 스크립트는 첫 스크립트를 개발하는 방법을 보여주기 위해 작성했다.
# 2008년 5월 1일
###################################################
# 변수 정의
HOME="/home/jthomas" # 간단한 홈 디렉터리
DATE=$(date +%H%M) # 콘 셸 명령인 date를 수행한 결과를 DATE에 저장한다.
HOSTNAME=$(hostname) # HOSTNAME은 hostname 명령을 수행한 결과를 저장한다.
##################
function if_error
##################
{
if [[ $? -ne 0 ]]; then # 함수에 전달하는 오류 코드를 점검한다.
print "$1" # if rc > 0이면 오류 메시지를 출력하고 끝낸다.
exit $?
fi
}
if [[ -e /tmp/file ]]; then # 먼저 파일이 존재하는지 살펴본다.
rm -f /tmp/file # 파일 삭제
if_error "Error: Failed removing file /tmp/file"
else
print "/tmp/file doesn't exist"
fi
if [[ -e /tmp/test ]]; then
mkdir /tmp/test # 디렉터리 test 생성
if_error "Error: Failed trying to create directory /tmp/test"
else
print "Directory exists, no need to create directory"
fi
case $TIME in
"2200")
rm -f /tmp/file1
;;
"2300")
rm -f /tmp/file1
;;
# 스크립트 끝
esac
|
스크립트를 실행하려면 명령행에서 ./scriptname.ksh를 입력한다.
명령행에서 스크립트에 입력 제공하기
Listing 15는 명령행에서 스크립트에 입력을 제공하는 방법을 보여준다.
Listing 15. 스크립트에 입력 제공하기
#!/bin/ksh
OPTION=$1
print "I love $OPTION"
$./scriptname milk
I love milk
$./scriptname tea
I love tea
$./scriptname "peanut butter"
I love peanut butter
|
스크립트 이름 다음에 입력하는 첫째 인수는 $1 변수에, 둘째 인수는 $2 변수에 저장된다. 스크립트 내에서는 $1, $2 등으로 인수 값을 확인하면 된다. 이런 변수를 사용하면 스위치나 옵션 등을 지원하는 유닉스 명령과 비슷한 형태로 콘 셸 스크립트를 작성할 수 있다.
스크립트에서 전자편지 보내기
스크립트로 몇몇 유형을 따르는 보고서를 작성해도 편리하다. 예를 들어, 시스템에 새로 등록되는 사용자를 매일 추척하려고 한다. 새로 등록되는 사용자를 파일에 쓴 후 전자편지를 보내주면 좋겠다. 그러면 매일 새로 추가되는 사용자 목록을 얻을 수 있다. 특정 파일을 전자편지로 보내는 코드는 다음과 같다.
$REPORT="/tmp/users"
cat $REPORT | mailx -s "User admin report from server XYZ" Jason_Thomas@kitzune |
위 코드는 $REPORT 파일 내용을 Jason_Thomas@kitzune에게 전자편지로 보낸다. -s는 전자편지 제목을 지정하는 스위치다. 정말 간단하지 않은가?
결론
콘 셸 스크립트는 시간과 노력을 덜어주는 아주 편리한 기능이다. 처음에는 어려워 보일지도 모르지만, 항상 간단하게 시작해 스크립트를 키워나가는 편이 좋다. 언제나 같은 단계를 따르라고 권한다. 먼저, 스크립트 머리글을 작성하고, 변수를 정의하고, 오류를 점검한다. 익숙해지면 모든 작업을 스크립트로 수행하려 할지도 모르겠다.
참고자료 교육
제품 및 기술 얻기
- KornShell: 콘 셸 홈페이지다. 소프트웨어와 자료를 제공한다.
- IBM 제품 평가판: 다양한 응용 프로그램 개발 도구와 미들웨어 제품을 developerWorks에서 다운로드해 사용해보자. DB2®, Lotus®, Rational®, Tivoli®, WebSphere® 등을 제공한다.
토론
필자소개  | |  | Kurt Riley는 2000년부터 유닉스와 리눅스 환경에서 일했으며, 포춘(Fortune) 500대 기업 여러 곳에 컨설팅 서비스를 제공한다. 정보 기술학 석사 학위를 소지한 그는 현재 월풀 사에서 Tivoli 보안 제품을 지원하고 있다. |
기사에 대한 평가
 |
| 이 문서 북마킹 하기
|
|