한번 더 셸 스크립트를 제대로 느껴보자
유닉스에는 길고 따분한 작업을 단순하게 만들기 위해 작업을 자동화하는 셸 스크립트가 있습니다. 이 기사는 셸 스크립트를 좀 더 제대로 배우기를 원하며 고급 스크립트를 작성하는 방법이 궁금한 독자들에게 팁을 제공합니다.
다른 유닉스 운영체제나 리눅스와 마찬가지로 IBM AIX 운영체제는 시스템 관리자, 개발자, 사용자가 일상 업무를 처리하고 고객 비즈니스를 단순하게 만들기 위한 여러 가지 강력한 도구를 제공한다. 유닉스에는 길고 따분한 작업을 단순하게 만들기 위해 작업을 자동화하는 셸 스크립트가 있다.
몇 년 동안 유닉스에서 셸 스크립트를 끼적거려 본 경험이 있을지라도 주로 운영체제 안팎을 파느라고 스크립트를 마스터하지는 못했을 것이다. 이 기사는 셸 스크립트를 좀 더 제대로 배우기를 원하며 고급 스크립트를 작성하는 방법이 궁금한 독자들에게 팁을 제공한다. 이 기사는 스크립트를 단순하게 만드는 방법, 스크립트를 최대로 유연하게 유지하는 방법, 깔끔한 스크립트를 작성하는 방법, 스크립트 내부에서 문서화 작업 방법, 스크립트 디버깅 방법을 포함해 일반적인 셸 프로그래밍 기본 원칙을 설명한다.
단순하게 만들기
사람들이 셸 스크립트를 제대로 배우려 할 때 이미 만들어진 스크립트를 복사해 사용하는 어리석음을 저지른다. 작업을 복사해 몇 가지 값을 코드 내부에서 수정하기보다는 스크립트 양쪽에서 모두 사용 가능하도록 함수를 만들 수 있다. 중앙 집중적인 함수를 만들면 스크립트를 표준화할 수 있다. 함수가 어떤 스크립트에서 제대로 동작하면 다른 스크립트에서도 동작하리라 확신할 수 있다.
예를 들어, Listing 1에 제시한 스크립트는 좀 더 작고 깔끔한 프로그램으로 압축해 단순하게 다듬어야 한다.
Listing 1. 단순하게 만들 수 있는 스크립트 예
#!/usr/bin/ksh
if [[ $# -lt 2 ]]
then
echo "Usage: ${0##*/}
exit 0
fi
if [[ ! -f "${1}" ]]
then
echo "Unable to find file '${1}'"
exit 1
fi
if [[ ! -r "${1}" ]]
then
echo "Unable to read file '${1}'"
exit 2
fi
gzip ${1}
ls -l ${1}.gz
if [[ ! -f "${2}" ]]
then
echo "Unable to find file '${2}'"
exit 1
fi
if [[ ! -r "${2}" ]]
then
echo "Unable to read file '${2}'"
exit 2
fi
gzip ${2}
ls -l ${2}.gz
|
이 스크립트는 보기만 해도 끔찍하다(다행히도 그래봐야 예제일 뿐이다). 이 스크립트를 최대로 압축해야 한다. 독자들의 눈을 즐겁게 만들기 위해 Listing 2에 좀 더 깔끔한 버전을 제시하겠다.
Listing 2. Listing 1의 스크립트를 압축한 예제
#!/usr/bin/ksh
exit_msg() {
[[ $# -gt 1 ]] && echo "${0##*/} (${1}) - ${2}"
exit ${1:-0}
}
[[ $# -lt 2 ]] && exit_msg 0 "Usage: ${0##*/}
for _FNAME in $@
do
[[ ! -f "${_FNAME}" ]] && exit_msg 1 "Unable to find file '${_FNAME}'"
[[ ! -r "${_FNAME}" ]] && exit_msg 2 "Unable to read file '${_FNAME}'"
gzip ${_FNAME}
ls -l ${_FNAME}.gz
done
|
차이점을 알겠는가? 모든 항목을 for 루프로 옮긴 다음에 메시지를 출력하고 적절한 반환 코드로 끝나는 간단한 함수를 추가하면, 스크립트는 깔끔해지고 이해하기도 쉬워진다.
유연하게 만들기
초보자가 셸 스크립트를 만드는 과정에서 직면하는 또 다른 문제는 셸 스크립트에 값을 정적으로 하드 코드하는 버릇이다. 이는 스크립트 유연성을 떨어뜨리므로, 나쁜 프로그래밍 습관이다. 관리자나 개발자가 스크립트를 계속해서 수정하지 않은 상태에서 다른 값을 집어 넣어 동작하도록 만들기 위해 변수를 활용하고 인수를 스크립트나 함수에 넘기자.
예를 들어, Listing 3은 잘못 작성되었기에 유연성이 떨어지는 스크립트 예다.
Listing 3. 유연하지 않은 스크립트 예
#!/bin/bash
if [[ -f /home/cormany/FileA ]]
then
echo "Found file '/home/cormany/FileA'"
elif [[ -f /home/cormany/DirA/FileA ]]
then
echo "Found file '/home/cormany/DirA/FileA'"
else
echo "Unable to find file FileA"
fi
|
이 스크립트는 돌아가긴 하지만 단지 두 위치에서 파일 하나만 탐색한다는 제약이 있다.
이런 생각을 확장해 똑같은 기능을 하지만 위치에 무관하게 파일을 탐색하는 스크립트를 Listing 4에 제시한다.
Listing 4. 스크립트를 좀 더 유연하게 만들기
#!/bin/bash
exit_msg() {
[[ $# -gt 1 ]] && echo "${0##*/} (${1}) - ${2}"
exit ${1:-0}
}
[[ $# -lt 2 ]] && exit_msg 1 "Usage: ${0##*/} "
_FNAME="${1}"
_DNAME="${2}"
[[ ! -d "${_DNAME}" ]] && exit_msg 2 "Unable to read or find directory '${_DNAME}'"
if [[ -f "${_DNAME}/${_FNAME}" ]]
then
exit_msg 0 "Found file '${_DNAME}/${_FNAME}'"
else
exit_msg 3 "Unable to find file '${_DNAME}/${_FNAME}'"
fi
|
이 예제가 좀 더 유연한 이유는 사용자가 찾고자 하는 디렉터리와 원하는 파일을 직접 입력할 수 있기 때문이다.
옵션을 주자
셸 스크립트를 작성할 때, "이런 기능이 있으면 정말 훌륭할 텐데!"나 "이렇게 할 수 있다면 좋겠어요!"라고 말하는 사람이 있는 반면에 해당 기능을 수행하기를 원하지 않기에 이런 의견에 동조하지 않는 사람도 있다. 사람들이 옵션을 좋아한다면, 옵션을 제공하면 된다. 내장 셸 명령어인 getopt 가 바로 이런 작업을 수행한다.
Listing 5는 AIX에서 getopt 가 동작하는 기본 원리를 보여준다.
Listing 5. getopt 예제
#!/usr/bin/ksh
_ARGS=`getopt -o x --long xxxxx -n ${0##*/} -- "$@"`
while [[ $# -gt 0 ]]
do
case "${1}" in
-x|--xxxxx) echo "Arg x hit!"; shift;;
--) shift; break;;
*) echo "Invalid Option: ${1}"; break;;
esac
done
|
opttest 라는 getopt 를 포함하는 스크립트를 수행할 때, -x 나 --xxxxx 같은 유효한 인수를 넘기면 getopt 는 스위치를 인식해 case 스위치 내부에 있는 코드를 다음과 같이 수행한다.
그리고 무효한 스위치나 옵션은 다음과 같이 처리한다.
# ./hm -a
Invalid Option: -a
|
문서화, 문서화, 문서화
인생을 살아오는 동안 어느 시점에서 문서에 사로잡힌 포로가 되기 마련이다. 퇴사한 누군가가 10년 전에 작성한 스크립트를 살펴봐야 하는 경우가 있다. "문제 없어요"라고 말할 수 있나? 일반적으로 문제는 아니다. 하지만 스크립트가 복잡하고 익숙하지 않은 명령을 수행하며, 익숙한 스타일에서 벗어나 다른 스타일로 만들어졌고, 게다가 동작조차 하지 않는다면, 원래 스크립트를 만든 개발자가 어떤 생각을 품고 있었는지를 안다면 엄청나게 도움이 될 것이다. 아니면 스크립트를 직접 개발했지만 한번만 쓰고 그 이후에는 다시 사용하지 않았을 경우도 있다. 아니면 몇 주 동안 거대한 스크립트를 작업해 안팎으로 내용을 알고 있지만 다른 누군가가 이 스크립트를 바라보면 완전히 헤매는 경우도 있다. 문서 작성이 사용자만큼이나 개발자에게도 중요한 이유는 사방에 널렸다.
Listing 6에 실어 놓은 코드 내용을 살펴보자.
Listing 6. 주석이 달려있지 않은 스크립트 예
confirm_and_exit() {
[[ ${_DEBUG_LEVEL} -ge 3 ]] && set -x
while [[ -z ${_EXIT_ANS} ]]
do
cup_echo "Are you sure you want to exit? [Y/N]
\c" ${_PROMPT_ERR_ROW} ${_PROMPT_ERR_COL}
${_TPUT_CMD} cnorm
read ${_NO_EOL_FLAG:+${_READ_FLAG:-'-n'}} ${_NO_EOL_FLAG} _EXIT_ANS
${_TPUT_CMD} civis
done
case ${_EXIT_ANS} in
[Nn]) unset _EXIT_ANS; return 0;;
[Yy]) exit_msg 0 1 "Exiting Script";;
*) invalid_selection ${_EXIT_ANS}; unset _EXIT_ANS;;
esac
return 0
}
|
셸 스크립트를 작업한 지 얼마 되지 않은 상황에서, 이 코드를 읽기란 어렵지 않다. 하지만 셸 스크립트를 막 배운 초보자가 이 코드를 읽으면 어떤 동작을 하는지 알지 못한다. 몇 분만 투자해 스크립트에 주석을 붙이면 완전히 다른 세상이 열린다. Listing 7은 동일한 함수에 주석을 단 모습이다.
Listing 7. 주석이 달린 스크립트 예
#########################################
# function confirm_and_exit
#########################################
confirm_and_exit() {
# _DEBUG_LEVEL을 3 이상으로 설정하면, 모든 평가 내용을 표준 출력으로 내보낸다.
[[ ${_DEBUG_LEVEL} -ge 3 ]] && set -x
# 유효한 대답을 할 때까지 사용자에게 질문을 계속한다.
while [[ -z ${_EXIT_ANS} ]]
do
# 스크립트를 나가기를 원하는지 사용자에게 물어본다.
# cup_echo 함수는 cup 를 호출한다.
# 구문:
# cup_echo
cup_echo "Are you sure you want to exit? [Y/N]
\c" ${_PROMPT_ERR_ROW} ${_PROMPT_ERR_COL}
# tput을 통해 커서를 정상으로 변경한다.
${_TPUT_CMD} cnorm
# 사용자가 입력한 값을 읽는다.
# _NO_EOL_FLAG가 켜져 있다면, _READ_FLAG나 "-n" 값을 사용한다.
# _NO_EOL_FLAG가 켜져 있다면, 사용자가 입력한 값을 그대로 _EXIT_ANS에 넣는다.
read ${_NO_EOL_FLAG:+${_READ_FLAG:-'-n'}} ${_NO_EOL_FLAG} _EXIT_ANS
# tput을 통해 커서를 감춘다.
${_TPUT_CMD} civis
done
# 사용자가 "n"을 입력했다면, 반환 코드 0으로 직전 블록으로 돌아간다.
# 사용자가 "y"를 입력했다면, 스크립트를 종료한다.
# 사용자가 다른 값을 입력했다면, invalid_selection 함수를 수행한다.
case ${_EXIT_ANS} in
[Nn]) unset _EXIT_ANS; return 0;;
[Yy]) exit_msg 0 1 "Exiting Script";;
*) invalid_selection ${_EXIT_ANS}; unset _EXIT_ANS;;
esac
# exit 함수는 반환 코드 0을 돌려준다.
return 0
}
|
앞서 보여준 작은 함수에 주석을 일일이 다는 작업은 따분하고 과잉인 듯이 보일지도 모르겠다. 하지만 초보 셸 스크립트 작성자나 함수를 살펴보는 다른 누군가에게 주석이 주는 가치는 매우 높다.
셸 스크립트에서 주석이 많은 도움을 주는 경우는 변수 설명과 반환 코드 설명이다.
Listing 8에 실어 놓은 예는 셸 스크립트 시작 부분이다.
Listing 8. 변수를 문서화하지 않은 예
#!/usr/bin/bash
trap 'exit_msg 1 0 "Signal Caught. Exiting..."' HUP INT QUIT KILL ABRT
trap 'window_size_changed' WINCH
_MSG_SLEEP_TIME=3
_RETNUM_SIZE=6
_DEBUG_LEVEL=0
_TMPDIR="/tmp"
_SP_LOG="${0##*/}.log"
_SP_REQUESTS="${HOME}/sp_requests"
_MENU_ITEMS=15
LESS="-P LINE\: %l"
export _SP_REQUESTS _TMPDIR _SP_LOG _DB_BACKUP_DIR
export _DEBUG_LEVEL _NEW_RMSYNC _RMTOTS_OFFSET_COL
|
다시 한번 이야기하지만, 트랩이 어떻게 동작하는지 각 변수가 무엇인지를 이해하기란 상당히 어렵다. 코드를 완벽하게 따라가기 전에는 각 변수는 의미가 없다. 또한 스크립트에서 사용한 반환 코드에 대한 언급도 없다. 이는 셸 스크립트에서 필요 이상으로 문제 해결을 어렵게 만들 가능성이 있다. Listing 8 에 있는 각 행에 몇 가지 주석을 달고, 사용된 반환 코드에 설명을 추가하면 혼동을 상당히 줄인다. 아래에서 Listing 9를 살펴보자.
Listing 9. 문서화된 변수 예
#!/usr/bin/bash
#########################################################################
# traps
#########################################################################
# 사용자가 스크립트를 떠나려고 시도할 때 트랩이 걸린다.
trap 'exit_msg 1 0 "Signal Caught. Exiting..."' HUP INT QUIT KILL ABRT
trap 'window_size_changed' WINCH # 사용자가 창 크기를 바꿀 때 트랩이 걸린다.
#########################################################################
#########################################################################
# 정의된/외부로 공개된 변수
#########################################################################
_MSG_SLEEP_TIME=3 # 모든 메세지를 위한 잠들기 시각(초 단위)
# (정의되어 있지 않다면 기본값은 1초다)
_CUSTNUM_SIZE=6 # 이 위치에서 고객 번호 길이
# (정의되어 있지 않다면 기본값은 6이다)
_DEBUG_LEVEL=0 # 로그 디버그 메시지. 로그 단계는 누적이다.
# (i.e. 1 = 1, 2 = 1 & 2, 3 = 1, 2, & 3)
# (정의되어 있지 않다면 기본값은 0이다)
# 로그 단계:
# 0 = 메시지 없음
# 1 = 간략한 메시지(시작 스크립트, 오류 등)
# 2 = 환경 설정(set / env)
# 3 = set -x (엄청난 메시지)
_TMPDIR="/tmp" # 임시 작업 파일을 담을 디렉터리
# (정의되어 있지 않다면 기본값은 /tmp다)
_SP_LOG="${0##*/}.log" # 스크립트 이벤트 로그
_SP_REQUESTS="${HOME}/sp_requests"
# 고객 레코드 요청을 위한 파일
# 시작 시점에서 읽힌다.
_MENU_ITEMS=15 # 페이지당 출력할 항목 기본 숫자
# (정의되어 있지 않다면 기본값은 10이다)
LESS="-P LINE\: %l" # 'less' 프롬프트 형식. 정보가 더 필요하면 MAN less 참조
# 위에서 정의한 변수를 외부에 공개한다.
export _MSG_SLEEP_TIME _CUSTNUM_SIZE _DEBUG_LEVEL _TMPDIR
_SP_LOG _SP_REQUESTS _MENU_ITEMS
#########################################################################
|
훨씬 나아 보이는가? 코드가 체계적으로 설명되어 있기에, 스크립트를 처음으로 읽는 누군가에게 프로그램이 어떻게 동작하는지 이해할 좋은 기회를 제공한다.
디버깅
스크립트 작성을 마쳤다면, 처음으로 프로그램을 실행할 시간이다. 하지만 스크립트를 실행할 때 몇 가지 예상치 못했던 오류가 출력된다. 하필 지금? 어느 누구도 완벽하지 않으며, 처음부터 스크립트를 작성해서 오류가 없는 지점까지 이르려면 상당한 시간과 경험이 필요하다. 경지에 오르더라도 글자를 빠뜨리거나 몇 글자를 바꾸는 바람에 혼이 난 경험이 있을 것이다. 하지만 걱정하지 말자. 유닉스와 리눅스에는 디버깅을 도와줄 셸이 있으니까.
예를 들어, Listing 10에 제시한 make_errors (이름처럼 동작한다)를 작성해 실행 준비를 마쳤다.
Listing 10. 오류가 있는 스크립트 예
#!/bin/bash
_X=1
while [[ ${_X} -le 10 ]]
do
[[ ${_X} -lt 5 ]] && echo "X is less than 5!
_Y=`expr ${_X) + 1`
if [[ ${_Y} -eq 6 ]]
echo "Y is now equal to ${_Y}"
fi
_X=${_Y}
done
|
하지만 스크립트를 처음 실행하면 다음과 같은 오류가 발생한다.
# ./make_errors
./make_errors: line 11: unexpected EOF while looking for matching `"'
./make_errors: line 16: syntax error: unexpected end of file
|
이미 사용하고 있으면서도 잘 모르는 강력한 디버깅 도구는 vim이다. vim은 강력한 텍스트 편집기지만 또한 디버깅에 도움을 주는 도구이기도 하다. .exrc나 .vimrc 파일에 특정 오류 조건을 색깔로 표시하는 기능을 설정해 놓았다면, 아래에 소개하는 그림 1에서 보여주듯이 vim이 디버깅을 지원한다.
그림 1. vim으로 디버깅하기
첫 번째 오류( line 11: unexpected EOF while looking for matching `"' )는 11행에서 뭔가 잘못되고 있음을 말해준다. 하지만 11행을 봐도 별로 잘못된 내용이 없어 보인다. 9행을 보자. echo 로 시작한 문자열 끝 부분에 " 기호가 빠져있다. 디버깅할 때 전체 스크립터를 살펴봐야 하는 이유를 제시하는 좋은 예다. 화면에 나타나는 행 번호는 항상 실제 오류가 발생한 곳이라고 보기는 어렵다. 11행이 오류로 나타난 이유는 9행이 "로 시작한 문자열을 감싸면서 시작했지만 11행까지 완전하게 감싸지는 못했기 때문이다. 이런 오류를 바로 잡으려면, 9행 끝에 " 기호를 붙인다.
하지만 뭔가 다른 오류가 바로 눈에 들어온다. 8행에서, 변수 _X 값 다음에 닫힌 괄호( ) )가 붉은 색으로 나타나 있다. vim이 자동으로 뭔가 잘못된 곳을 찾아 보여주는 예다. 변수 _X 값이 { 기호로 시작했지만 } 기호로 올바르게 닫히지 않은 듯이 보인다. ) 기호를 } 기호로 바꾸면 끝난다.
참 잘했다. 지금까지 오류 두 개를 고쳤다. 스크립트를 다시 한번 수행해 무슨 일이 벌어지는지 살펴보자.
./make_errors: line 12: syntax error near unexpected token `fi'
./make_errors: line 12: ` fi'
|
또 다른 오류가 발생했다. 이 오류는 12행에 문제가 있다고 알려주지만 if 구문을 끝내는 fi 가 있을 뿐이다. 뭐가 잘못되었을까? 직전 오류를 다시 한번 상가하자. 셸이 보고하는 오류 행에서 반드시 오류가 발생하지는 않는다. 셸은 단순히 오류가 발생한 위치만 보고하므로, 여기가 셸이 실패를 보고하기 앞서 오류가 시작된 곳을 의미하지는 않는다. 작은 스크립트에서는 실제 if 구문에 오류가 있을지도 모른다고 가정해도 좋다. 기본 셸 스크립트 논리로 돌아가서 생각해보면, if 구문은 if 와 fi 를 포함한다. 제어 구문을 보면 then 이 빠진 듯이 보인다. 여기서는 then 을 스크립트에 추가하면 끝난다. 작업을 마쳤다면 스크립트는 Listing 11과 같다.
Listing 11. Listing 10을 수정한 코드
#!/bin/bash
_X=1
while [[ ${_X} -le 10 ]]
do
[[ ${_X} -lt 5 ]] && echo "X is less than 5!"
_Y=`expr ${_X} + 1`
if [[ ${_Y} -eq 6 ]]
then
echo "Y is now equal to ${_Y}"
fi
_X=${_Y}
done
|
스크립트를 한번 더 돌리자.
# ./make_errors
X is less than 5!
X is less than 5!
X is less than 5!
X is less than 5!
Y is now equal to 6
|
축하한다! 이제 스크립트는 기대한 바와 같이 동작한다.
set -x 옵션
종종 셸 스크립트에서 기초적인 문제 해결은 위에서 든 예처럼 쉽지 않다. 다른 모든 시도가 실패해서 벽에 머리를 찧고 있어도, 스크립트 실패 원인을 파악하지 못한다면 마지막 수단으로 대포를 꺼내자. ksh이나 bash 같은 현대적인 셸은 set 명령어에 -x 스위치를 포함하고 있다. set -x 옵션을 사용하면, 평가된 모든 명령어가 확장되면서 표준 출력으로 나온다. 평가된 코드를 구분하기 위해 출력되는 모든 코드 행의 앞쪽에 PS4로 지정한 문자열을 표시한다. 상당히 많은 텍스트가 출력되므로 심호흡을 하고 천천히 살펴보자.
직전 예제에서 루프 횟수를 따지기 위해 주석과 함께 스크립트 첫 부분에 set -x 를 추가해 실행한다. Listing 12를 살펴보자.
Listing 12. set -x 예제
#!/bin/bash
set -x
# 루프를 돌면서 몇 가지 테스트 구문을 출력한다.
_X=1
while [[ ${_X} -le 4 ]]
do
[[ ${_X} -lt 2 ]] && echo "X is less than 2!
_Y=`expr ${_X} + 1`
if [[ ${_Y} -eq 3 ]]
then
echo "Y is now equal to ${_Y}"
fi
_X=${_Y}
done
|
스크립트 수행에 앞서 PS4를 다른 출력 결과물과 구분이 가능한 문자열로 바꾼다.
이제 Listing 13에서 보여주듯이 백사장에서 바늘 찾는 작업을 시작하자.
Listing 13. set -x 출력 결과 예
# ./make_errors
DEBUG => _X=1
DEBUG => [[ 1 -le 4 ]]
DEBUG => [[ 1 -lt 2 ]]
DEBUG => echo 'X is less than 2!'
X is less than 2!
DDEBUG => expr 1 + 1
DEBUG => _Y=2
DEBUG => [[ 2 -eq 3 ]]
DEBUG => _X=2
DEBUG => [[ 2 -le 4 ]]
DEBUG => [[ 2 -lt 2 ]]
DDEBUG => expr 2 + 1
DEBUG => _Y=3
DEBUG => [[ 3 -eq 3 ]]
DEBUG => echo 'Y is now equal to 3'
Y is now equal to 3
DEBUG => _X=3
DEBUG => [[ 3 -le 4 ]]
DEBUG => [[ 3 -lt 2 ]]
DDEBUG => expr 3 + 1
DEBUG => _Y=4
DEBUG => [[ 4 -eq 3 ]]
DEBUG => _X=4
DEBUG => [[ 4 -le 4 ]]
DEBUG => [[ 4 -lt 2 ]]
DDEBUG => expr 4 + 1
DEBUG => _Y=5
DEBUG => [[ 5 -eq 3 ]]
DEBUG => _X=5
DEBUG => [[ 5 -le 4 ]]
|
출력 결과를 보면 알겠지만 상당히 많은 정보가 화면에 표시된다. 모든 명령어를 평가해 수행한다. 셸 스크립트에서 주석 영역이 디버깅 정보에 출력되지 않음에 주목하자. 문자열이 주석이므로 평가 이후에 수행되지 못하기 때문이다. 다행스럽게 수정 이후에 이 스크립트에 잘못된 사항은 없어 보인다.
set -x 를 사용할 때 한 가지 주의해야 하는 사실이 있다. 평가하려는 스크립트가 내부 함수라면 set -x 를 코드 루트 본체에 둘 경우 자식 함수에 영향을 미친다는 사실이다. 하지만 set -x 를 내장 함수에 놓아두면, 단지 내부 함수 내부에서 호출되는 코드와 함수만 디버그 옵션을 따른다. 이럴 경우 셸 스크립트 루트 본체에서 디버그 옵션을 따르지 않는 이유는 이 루틴을 호출하는 자식 함수를 모르기 때문이다.
결론
우리는 항상 프로그래밍 기법을 개선한다. 셸 스크립트이거나 C이거나 자바(Java™)이거나 아니면 현재 작업 중인 다른 프로그래밍 언어이거나 무관하게 말이다. 작업을 단순하게 만들고 깔끔하고 유연하게 코드를 작성하며, 문서로 만드는 기본 규칙을 철저하게 따르면, 이 기사에서 배운 디버깅 기법을 활용해 최상급 셸 스크립트를 작성할 수 있을 것이다. 행운을 빈다!
필자소개
Adam Cormany는 현재 National Data Center의 관리자로 일하고 있다. Cormany는 유닉스 시스템 엔지니어, 유닉스 관리자, Scientific Games Corporation에서 운영 관리자를 맡아왔다. Cormany는 10년 넘게 솔라리스, 레드햇 리눅스 관리자는 물론이고 AIX도 광범위하게 다뤘다. Cormany는 pSeries AIX 시스템 관리 분야에서 IBM eServer 공인 전문가다. 관리 업무 이외에, Cormany는 배시, csh, ksh을 사용한 셸 스크립트와 C, PHP, 펄 프로그래밍에도 광범위한 지식을 습득했다.
출처 : 한국 IBM
제공 : DB포탈사이트 DBguide.net
댓글