반응형

필자 서문


프로그래머들은 솔라리스 제품을 사용하기 시작할 때 프로그래밍 스크립트를 즉시 시작하기를 원합니다. 처음에는 효율성과 간결성에 대해서는 관심들이 없고 오로지 그 결과에만 관심을 가지고 있습니다. 본 기술 자료에서는 신속한 시작을 위해 검증된 쉘 프로그래밍 기법에 대해 설명합니다. 경험이 쌓일수록 자신만의 프로그래밍 스타일을 개발하고 스크립트의 효율성과 간결성을 향상시킬 수 있을 것입니다.

배경


command shell 은 사용자와 상호 작용하고 운영 시스템과 통신하는 레이어입니다 MS-DOS를 사용할 때 대부분의 사람들은 command.com 쉘을 사용하지만, COMSPEC 환경 변수를 통해 다른 쉘이 지정될 수도 있습니다.

이와 유사하게, 각 UNIX 사용자는 UNIX로 전달하기 위해 사용할 명령 쉘을 선택해야 합니다. UNIX 계정이 설정될 때 시스템 관리자는 사용자의 기본 쉘을 선택합니다. 표준 옵션은 Bourne Shell(/bin/sh), C-Shell(/bin/csh), Korn Shell(/bin/ksh), Bourne-Again Shell(/bin/bash)입니다. 많은 개발자들이 C와 유사한 구문 때문에 C-쉘을 사용하지만 이는 주관적인 선택이며, 여기에서는 Korn 쉘만을로 사용합니다. 다른 쉘에서는 구문이 세대로 실행되지 않을 수도 있습니다.

명령행에서 쉘 스크립트를 실행하면 기본 쉘이 사용됩니다. 기본 쉘이 Korn이라면 기술 자료에 있는 스크립트는 구문 오류 없이 실행됩니다. 하지만 스크립트를 실행하기 위해 다른 쉘을 원한다면 어떻게 해야 할까요? 스크립트가 항상 Korn 쉘을 사용해 실행되도록 하려면 사용자의 기본 쉘에만 의존할 수 없습니다. 해결책은 스크립트의 첫 행에 스크립트가 어느 쉘 아래에서 실행될지 표시되는 UNIX 기능을 사용하는 것입니다. 코드 예제 1의 구문은 현재 사용자가 어느 쉘을 사용하든 상관없이 스크립트가 Korn 쉘을 사용해 실행되도록 지시합니다.

#!/bin/ksh

# your script goes here. All lines starting with # 
# are treated as comments
코드 예제 1 - 스크립트가 Korn 쉘에 의해 실행되도록 지정합니다.

일부 문서에서는 표 1에 나타난 것처럼 현재 쉘을 표시하기 위해 다른 명령 프롬프트 기호를 사용합니다. (필자가 가장 선호하는 쉘이 Korn 쉘이기 때문에 본 기술 자료에 나오는 모든 예제는 $-prompt를 사용합니다.) 스크립트가 항상 Korn 쉘을 사용하여 실행되도록 할 수 없기 때문에 각 스크립트의 첫 행으로 #!/bin/ksh를 입력합니다. (본 기술 자료의 $-prompt는 단지 명령이 명령행에 입력되고 있음을 나타냅니다.)

프롬프트 쉘l
$ Bourne 또는 Korn 쉘
% C-shell
# Root login

표 1 - UNIX 프롬프트 기호

스크립트 작성 - 기초


UNIX 스크립트 파일은 DOS BAT 파일과 유사합니다. DOS에서의 프로그래밍 권장 사항과 비권장 사항은 모두 UNIX에도 적용됩니다.

스트립트 작성과 관련된 단계는 다음과 같습니다:

  1. 쉘 프롬프트에서 UNIX 명령을 대화식으로 실행합니다.
  2. UNIX 명령을 포함하는 쉘 스크립트를 작성합니다.
  3. 쉘 스크립트를 실행 가능하도록 만듭니다.
  4. 스크립트를 테스트합니다.
  5. 스크립트를 실행합니다.
    1. 대화식으로
    2. 미래의 어느 일자 및 시간에 단 한 번
    3. 고정된 일정에 따라 반복적으로
    4. HTML 형식 사용

간단한 스크립트 작성


vmstat 정보를 확보하기 위한 스크립트를 작성하고자 한다고 가정해 보십시오. 1분 동안 2초 간격으로 vmstat를 실행하고자 합니다. 위에 설명된 5단계를 사용해 목표를 달성할 수 있습니다.

먼저, man vmstat를 사용해 vmstat에 대한 문서를 조회합니다. 그 다음, 명령을 대화식으로 실행해 구문과 예상되는 출력을 파악합니다. 코드 예제 2는 vmstat를 2초 간격으로 30회 실행하는 구문을 보여줍니다.

$  vmstat  2  30
코드 예제 2 - vmstat 명령을 2초 간격으로 30회 대화식으로 실행

쉘 스크립트 작성


다음으로, 명령을 포함하는 스크립트 파일을 작성합니다. 스크립트 위치와 스크립트 이름을 설명하는 표준을 확립해야 합니다. 예를 들어 회사와 같은 특정 카테고리의 모든 사항을 /usr/local 아래의 하위 디렉토리에 저장합니다. 이 예제에서는 회사를 Acme Products로 가정하므로 디렉토리는 /usr/local/acme입니다. 이 디렉토리 내에 scripts라는 하위 디렉토리와 logs라는 하위 디렉토리를 만드십시오. 목적에 따라 다른 하위 디렉토리가 필요할 수도 있습니다.

다음으로, vi와 같은 텍스트 편집기를 사용해 capture_vmstat.sh라는 스크립트 파일을 작성합니다. EXE, COM, BAT가 실행 가능한 파일을 나타내는 DOS와 달리, UNIX에서는 파일 확장자의 의미가 없습니다. .sh를 확장자로 사용해 쉘 스크립트 파일을 표시할 수 있지만 스크립트를 실행 가능하도록 만들지는 못합니다. 파일에 대한 이 명명 규칙을 통해 보다 쉽고 신속하게 파일을 식별할 수 있습니다. 또한, 파일 이름이 표준을 준수한다면 find 명령을 사용해 특정 유형의 모든 파일을 찾을 수 있습니다.

스크립트 파일은 두 개의 행을 보유합니다. 첫 번째 행은 일말의 여지를 두지 않고 Korn 쉘이 이 스크립트에 있는 명령을 실행하도록 지정합니다. 두 번째 행은 UNIX 명령 그 자체입니다. 코드 예제 3은 capture_vmstat.sh 스크립트의 전체 목록입니다.

#!/bin/ksh

vmstat  2  30
코드 예제 3 - vmstat를 2초 간격으로 30회 실행하는 capture_vmstat.sh 스크립트

쉘 스크립트를 실행 가능하도록 만들기


파일 확장자를 이용해 파일의 실행 가능성 여부를 판단하는 DOS와 달리, UNIX는 파일 접근 권한(file permission)에 의존합니다. chmod 명령은 파일을 실행 가능으로 표시하는 데 사용됩니다.

execute-bit를 설정하는 가장 간단한 방법은 chmod +x capture_vmstat.sh를 사용하는 것입니다. 현업 환경에서는 공개된 서버에서 스크립트에 대한 전체 액세스를 제어하기 위해 소유자, 그룹 및 세계 권한(world permission)을 고려해야 합니다. (파일 접근 권한 주제는 본 문서의 범위를 벗어납니다.) 보다 자세한 정보는 man chmod를 참조하십시오.

쉘 스크립트 테스트


이제 스크립트를 테스트할 준비가 되었습니다. DOS와 달리, UNIX는 실행할 파일을 현재 디렉토리에서 자동으로 찾지 않습니다. UNIX는 PATH 환경 변수를 제공하며 단지 PATH 변수에서 확인된 디렉토리에서 실행 파일을 검색합니다. 대부분의 사람들이 PATH에 현재 디렉토리를 포함시키지 않아(점이 현재 디렉토리를 나타냄) /usr/local/acme/scripts가 PATH에 존재하지 않기 때문에 코드 예제 4의 명령을 입력하는 것만으로는 실행되지 않습니다.

$  cd /usr/local/acme/scripts

$  capture_vmstat.sh
코드 예제 4 - "."이 PATH에 없으면 스크립트가 실행되지 않습니다

경로를 포함하여 스크립트의 전체 파일명을 명시적으로 지정해야 합니다. 나중에 변경되거나 둘 중 하나가 잘못될 가능성이 있으므로 PATH 변수에 의존하지 마십시오. 우선, 스크립트가 있던 디렉토리가 실수로 PATH에서 제거될 수 있으며 이 경우 UNIX는 더 이상 스크립트를 찾지 못할 것입니다. 심지어 UNIX는 새로운 PATH에 있는 다른 디렉토리에서 동일한 이름을 가진 스크립트를 찾아 실행할 수도 있습니다. 그러므로 안전을 위해서는 코드 예제 5에서처럼 전체 파일명을 지정하여 스크립트를 실행해야 합니다.

$  /usr/local/acme/scripts/capture_vmstat.sh
코드 예제 5 - UNIX가 올바른 스크립트를 찾을 수 있도록 전체 파일명 지정

일일이 입력하기가 번거로우므로 "."(점)이 현재 디렉토리를 나타낸다고 가정합니다. 우선, 스크립트 디렉토리로 변경한 다음 코드 예제 6에서처럼 스크립트명에 "./"(점-슬래시)를 덧붙여 스크립트를 실행합니다. 스크립트 하나만 실행하는 경우에는 별 차이가 없지만 스크립트 디렉토리에서 여러 개의 스크립트를 실행하는 경우에는 디렉토리명을 한 번만 입력하면 되는 장점이 있습니다.

$  cd /usr/local/acme/scripts

$  ./capture_vmstat.sh
코드 예제 6 - 점-슬래시(./) 표기를 사용해 스크립트 실행

capture_vmstat.sh 스크립트를 어떤 방법으로 호출하든 간에, vmstat를 대화식으로 실행할 때 얻은 결과와 출력이 동일해야 합니다.

스크립트 실행


이제 스크립트가 작성되었고 그 작동 방식을 알고 있습니다. 스크립트를 다음 네 가지 방법으로 실행할 수 있습니다:

1.대화식


스크립트를 문서화하여 다른 사람(헬프 데스크 직원 등)이 스크립트 파일을 실행할 수 있도록 합니다. DOS 사용자가 그들을 위해 만들어진 BAT 파일을 사용하기 위해 DOS 명령이나 구문을 이해할 필요가 없는 것과 매한가지로, 스크립트를 실행하는 사람들이 UNIX 명령이나 구문을 알아야 할 필요는 없습니다.

2.at 명령 사용


at 명령을 사용해 향후 언젠가 한번 스크립트를 실행합니다. 자세한 내용은 man at을 확인하십시오. 일부 UNIX 시스템은 사용자가 로그아웃하면 at-jobs 실행을 취소합니다. 시스템 문서를 신중하게 확인합니다.

3.cron 유틸리티 사용


crontab 파일을 사용해 고정된 일정에 따라 스크립트를 반복적으로 실행합니다. 자세한 내용은 man crontab을 확인합니다. 코드 예제 7은 월/수/금요일마다 오전 8시-오후 5시까지 한 시간에 한번 X시 10분에 스크립트를 실행하는 간단한 crontab 입력을 보여줍니다:

10   8-17   *   *  1,3,5  /usr/local/acme/scripts/capture_vmstat.sh
코드 예제 7 - capture_vmstat.sh를 실행하는 crontab 입력 script

스크립트를 실행하기 위한 네 번째 방법으로 넘어가기 전에, crontab을 통해 스크립트를 실행할 경우 생기는 두 가지 문제를 이해해야 합니다. 첫째, 스크립트가 실행될 때 로그인하지 않았기 때문에 기본 쉘이 되는 Korn 쉘에 의존할 수 없습니다. 그러므로, 코드 예제 1에 설명된 것처럼 #!/bin/ksh를 스크립트의 첫 행으로 사용하도록 해야 합니다. 둘째, 현재 버전의 스크립트는 터미널로 출력을 전송합니다. cron이 스크립트를 실행할 때 터미널이 없으므로, cron은 stdout의 경로를 다른 곳으로 재지정해야 합니다. 정상적인 위치는 crontab으로 스크립트를 실행한 사용자의 e-메일 수신함(inbasket)입니다. 이 방법도 괜찮지만, 기본 스크립트를 확장할 때 아래에 설명된 다른(더 나은) 해결책을 사용할 수 있습니다.

4.HTML 형식 사용


HTML 형식을 사용해 스크립트를 실행하고 CGI(common gateway interface)를 통해 스크립트를 게시합니다. 명령의 출력은 브라우저로 다시 전송되므로 <PRE>와 </PRE> HTML 태그는 서식을 보존하는 데 사용되어야 합니다.

이 HTML 형식 메소드는 여기서 설명한 것보다 많으며, FORM과 CGI를 사용할 경우 수많은 보안 위험이 존재합니다. 그러나, 이 메소드는 사내 헬프 데스크 직원이나 기타 레벨1 지원 인력이 사용하기에 매우 성공적인 것으로 입증되었습니다.

간단한 스크립트 확장


이전 스크립트는 새로운 프로그래밍 언어를 배울 때 작성되는 최초의 표준 프로그램인 "hello, world"의 쉘 스크립트 버전이었습니다. 이제 몇 가지 기본 기능을 여기에 추가할 수 있습니다.

stdout 경로 재지정


먼저, 스크립트는 출력을 stdout으로 전송합니다(정상적으로는 터미널임). 스크립트를 확장해 코드 예제 8에서처럼 출력 경로를 로그 파일로 재지정할 수 있습니다.

#!/bin/ksh

vmstat  2  30  >  /usr/local/acme/logs/vmstat.log
코드 예제 8 - stdout 경로를 파일로 재지정

그러나 이로 인해 몇 가지 새로운 문제가 발생합니다. 우선, 스크립트를 실행할 때마다 마지막 로그 파일의 내용을 덮어쓰게 됩니다.+ 이 문제를 해결하려면 기존 로그 파일의 끝에 새로운 출력을 덧붙입니다. 파일에 있는 date-time stamp는 마지막 로그가 작성된 시점만을 표시하므로 이제 로그에 있는 각 출력이 작성된 시점을 알아야 합니다.

스크립트 내 하위 명령 실행

스크립트의 각 실행보다 앞서는 파일에 현재의 일자와 시간을 기록합니다. 기존 파일을 덮어쓰는 대신 >>를 사용해 파일 끝에 출력을 덧붙입니다. 코드 예제 9에서, find 및 find-next를 사용해 파일을 검사하기 쉽도록 독립적으로 파악할 수 있는 문자를 열 1에 놓습니다. 또한 로그 파일에 현재의 일자와 시간을 기록할 수도 있습니다. 코드 예제 9 $(date)는 Korn 쉘이 date 명령을 실행하고 출력을 echo 명령행으로 배치하도록 지시합니다. UNIX 명령을 실행하고 출력을 사용하려고 할 때마다 $를 입력하고 괄호 안에 명령을 넣으십시오.

#!/bin/ksh

echo "#--- $(date)"  >> /usr/local/acme/logs/vmstat.log

vmstat  2  30	>> /usr/local/acme/logs/vmstat.log
코드 예제 9 - 로그 파일에 stdout 추가

코드 예제 10에서 Korn 쉘은 netstat 명령, grep for ESTABLISH를 실행하고, 이들 명령을 $(xxx) 안에 넣어 행 수를 세는 데 wc를 사용하도록 지정될 수 있습니다. 나아가 Korn 쉘은 이러한 명령의 출력을 환경 변수 CTR_ESTAB에 저장하도록 지시됩니다. 그 다음, echo 명령에서 Korn 쉘은 해당 환경 변수에 저장된 값을 사용하도록 지시됩니다. 환경 변수에 저장된 값을 사용하려면 변수 이름 앞에 $를 넣습니다(예: $CTR_ESTAB). 가독성을 높이고 모호성을 방지하려면 중괄호 안에 변수 이름을 넣는 Korn 쉘 옵션을 사용합니다(예: ${CTR_ESTAB}).

# store current date as YYYYMMDD in variable DATE for later 
# use

export DATE=$(date +m%d)

# count number of established socket connections and write. 
# to log

export CTR_ESTAB=$(netstat -na -P tcp | grep ESTABLISH | wc 
  -l)

export CTR_CLOSE_WAIT=$(netstat -na -P tcp | grep CLOSE WAIT 
  | wc -l)

echo "${DATE} ${CTR_ESTAB} ${CTR_CLOSE_WAIT} >> ${LOG_FILE}
코드 예제 10 - $(xxx)를 사용해 Korn 쉘 스크립트 내 명령 실행

고유 파일명 생성


여러 사용자가 스크립트를 동시에 실행하면 어떤 일이 발생할까요? 스크립트의 각 인스턴스는 동일한 출력 파일에 기록할 것이므로, 각 스크립트의 출력은 출력 파일에 인터리브됩니다. 코드 예제 11에서처럼 파일 이름에 PID 번호($$로 표현)를 넣어 고유한 출력 파일명을 작성할 수 있습니다.

#!/bin/ksh

echo "#--- $(date)" 
  >> /usr/local/acme/logs/vmstat.$$.log

vmstat  2  30	>> /usr/local/acme/logs/vmstat.$$.log
코드 예제 11 - $$를 사용해 현재 PID를 사용하는 고유한 파일명 생성

다음 사용자가 스크립트를 실행할 때 다른 PID가 스크립트 실행에 할당되기 때문에 기존 로그 파일에 추가되는 것이 아니라 매번 별도의 로그 파일을 작성하게 됩니다 잘못된 것은 아니지만 이런 결과를 원하지도 않았습니다.

스크립트가 실행될 때마다 값이 변경되는 환경 변수를 사용하는 대신 스크립트 실행 이전에 스크립트 외부에서 한 번 설정된 환경 변수를 사용하는 방법도 생각해볼 수 있습니다. UNIX는 사용자가 로그인할 때마다 LOGNAME 환경 변수를 설정합니다. 코드 예제 12에서, 이 값은 각 사용자가 로그 파일을 가질 수 있도록 로그 파일명에 들어갑니다:

#!/bin/ksh

echo "#--- $(date)"	
  >> /usr/local/acme/logs/vmstat.${LOGNAME}.log

vmstat  2  30		
  >> /usr/local/acme/logs/vmstat.${LOGNAME}.log
코드 예제 12 - 값이 외부에서 설정된 환경 변수를 사용해 파일명 생성

구조적 프로그래밍 기법


두 번의 최종 마무리 점검을 거쳐 기본 Korn 쉘 스크립트를 완료합니다. 첫째, vmstat 명령의 빈도 또는 기간을 변경하고자 하는 경우 어떻게 될까요? vmstat 명령의 간격과 기간을 하드 코딩(hard-coding)하는 대신 명령행 인수를 사용해 해당 값을 받아들일 수 있습니다. 이러한 인수는 vmstat 명령이 인수를 액세스할 수 있는 환경 변수에 저장될 수 있습니다. 물론, 사용자가 명령행을 사용해 값을 제공하지 않는 경우 스크립트는 기본값을 제공해야 합니다.

둘째, 로그 파일 명명 규칙에 대한 생각을 변경하면 어떻게 됩니까? 명명 규칙은 사용자에게 명령행 인수를 사용할 때마다 제공할 것을 요구하는 사항이 아닙니다. 그러나, 스크립트의 여러 행에 하드 코딩된 로그 파일명이 있을 경우 다른 명명 규칙을 사용하기로 결정하면 이름이 지정된 위치를 확인하기 위해 스크립트의 모든 행을 검색해야 합니다.

그 대신, 환경 변수에 로그 파일명을 저장하고 변수에 포함된 파일명에 출력을 덧붙이도록 각 명령을 수정하십시오. 그러면 로그 파일 명명 규칙을 변경할 때 환경 변수가 설정된 한 행을 수정하기만 하면 됩니다.

#!/bin/ksh
# ----------------------------------------------------
# capture_vmstat.sh	<INTERVAL> <COUNT>
#	<INTERVAL> vmstat interval
#	<COUNT>	vmstat count
# run vmstat and capture output to a log file
#-----------------------------------------------------

# indicate defaults for how often and for how long 
# to run vmstat
export INTERVAL=2		# every 2 seconds
export COUNT=30		# do it 30 times

# obtain command line arguments, if present
if [ "${1}" != "" ]
then
	INTERVAL=${1}
	# if there is one command line argument, 
	# maybe there's two
	if [ "${2}" != "" ]
	then
	COUNT=${2}
	fi
fi

# directories where scripts and logs are stored
export PROGDIR=/usr/local/acme/scripts
export LOGDIR=/usr/local/acme/logs

# define logfile name and location
export LOG_FILE=${LOGDIR}/capture_vmstat.${LOGNAME}.log

# write current date/time to log file
echo "#--- $(date)"		>> ${LOG_FILE}
vmstat  ${INTERVAL}  ${COUNT}	>> ${LOG_FILE}

# say goodnight, Gracie
exit 0
코드 예제 13 - capture_vmstat.sh 스크립트의 더욱 강력한 버전

for-loop 스크립트 작성


객체 목록에 대해 단일 명령을 실행하고자 하는 경우가 있습니다. 예를 들어, rsh 명령을 사용해 여러 서버에 대해 동일한 명령을 원격으로 실행하려 할 수도 있습니다(자세한 내용과 r-명령을 사용할 때의 보안 위험에 대해서는 man rsh를 참조합니다). .

한 가지 기법은 환경 변수에 LIST라고 하는 객체 목록을 저장하는 것입니다. 그러면 for loop를 사용해 rsh 명령을 반복적으로 실행할 수 있으며, 각 루프는 LIST에 다음 값을 보유하게 됩니다. 코드 예제 14는 for-loop 스크립트 샘플을 보여줍니다.

#!/bin/ksh

export LIST="bvapp1 bvapp2 bvapp3"

export LOG=/usr/local/acme/logs/throw_away.log

for SERVER in ${LIST}
do
  # each loop has a different value for ${SERVER}
  echo "#------- values from ${SERVER}" >> ${LOG}
  rsh  ${SERVER} 
  "ps -f -u bv -o pid,pmem,pcpu,rss,vsz" >> ${LOG}
done

# say goodnight, Gracie
exit 0
코드 예제 14 - 간단한 for-loop 스크립트

while-loop 스크립트 작성


경우에 따라 단일 명령을 실행하고 잠시 기다린 다음 명령을 다시 실행하기를 원할 수도 있습니다. 때로는 이 루프가 무제한 계속되기를 원할 수도 있고 때로는 루프가 한정된 횟수 만큼 실행된 다음 종료되기를 원합니다.

사용자 bv 아래에서 실행되는 프로세스를 모니터링하려고 하며 2시간 동안 10분마다 bv를 모니터링하기를 원한다고 가정해 보겠습니다. 우선, 코드 예제 15에 나오는 코드를 사용해 명령을 대화식으로 테스트합니다(자세한 내용은 man ps를 참조합니다):

ps  -f -u bv  -o  pid,pcpu,pmem,rss,vsz,comm
코드 예제 15 - -o 인수를 사용하는 대화식 ps 명령

이제 이 명령을 루프에서 실행하는 스크립트 파일을 작성해야 합니다. 루프는 ps 명령 실행 간의 10초 동안 일시 정지하고 720회 실행해야 합니다[2시간 동안 10초마다 한번, 즉 분당 6회 또는 시간당 360회(60분/시 * 6/분)]. 코드 예제 16은 간단한 while-loop 스크립트를 보여줍니다.

#!/bin/ksh

export INTERVAL=10
export COUNT=720

export LOG=/usr/local/acme/logs/while_loop_test.log

export CTR=0
while [ true ]
do
	if [ ${CTR} -ge ${COUNT} ]
	then
		exit
	fi
	echo "#------- $(date +m%d-03/24/03M%S)"	
          >> ${LOG}
	ps  -f  -u  bv  -o  pid,pcpu,pmem,rss,vsz,comm	
          >> ${LOG}
	CTR=$(expr ${CTR} + 1)
	sleep ${INTERVAL}
done
코드 예제 16 - 간단한 while-loop 스크립트

코드 예제 17은 출력 로그 파일의 일부분을 보여줍니다.

#------- 19991203-123237
  PID %CPU %MEM  RSS  VSZ COMMAND
12007  0.2  0.8 13640 24280 cmsdb
11938  0.0  0.7 11536 20496 sched_poll_d
<snip>
#------- 19991203-123240
  PID %CPU %MEM  RSS  VSZ COMMAND
12007  0.2  0.8 13640 24280 cmsdb
11938  0.0  0.7 11536 20496 sched_poll_d
<snip>
#------- 19991203-123243
  PID %CPU %MEM  RSS  VSZ COMMAND
12007  0.3  0.8 13640 24280 cmsdb
11938  0.0  0.7 11536 20496 sched_poll_d
<snip>
#------- 19991203-123246
<and-so-on>
코드 예제 17 - while-loop 스크립트의 출력

퀵 레퍼런스 카드(Quick Reference Card)


아래 프로그래밍 팁과 기법은 본 기술 자료에 제시된 프로그래밍 스타일과 방법론에 대한 퀵 레퍼런스로서 여기에서 다룬 주제에 대한 퀵 레퍼런스 버전을 확인할 수 있을 것입니다.

1. 항상 다음과 같은 행으로 스크립트를 시작합니다


#!/bin/ksh

2. 항상 변수를 정의할 때 대문자를 사용합니다. 소문자를 사용해 단어를 분리합니다.


BIN_DIR=/opt/bv1to1/bin

3. 하위 프로세스가 값을 자동으로 액세스하도록 항상 환경 변수를 익스포트합니다:


export SUPPORT_IDS="userA@domain.com,userB@domain.com

4. UNIX 명령을 실행하고 Korn 쉘 스크립트의 어딘가에 출력을 사용하려면 $를 입력하고 괄호 안에 명령을 넣은 다음 출력을 환경 변수에 저장합니다.


export CTR_ESTAB=$(netstat -na | grep ESTABLISH | wc -l)

5. 환경 변수에 저장된 값을 사용하려면 변수명 앞에 $를 넣습니다. 가독성을 높이고 모호성을 방지하려면 중괄호 안에 변수 이름을 넣습니다.


echo "The number of ESTABLISHED connections is ${CTR_ESTAB}"

6. 고유한 파일명을 갖게 하려면 $$를 사용해 파일명에 PID 번호를 포함시킵니다. 파일 확장자 바로 앞의 파일명에 PID 번호를 삽입합니다:


export LOG_FILE=/tmp/capture_vmstat.$$.log

7. chmod +x 파일명을 사용해 스크립트 파일을 실행 가능하도록 만듭니다.


chmod  +x  capture_vmstat.sh

8. 스크립트가 현재 디렉토리에 있다는 것을 UNIX가 알 수 있도록 스크립트 이름 앞에 점-슬래시(./)를 넣습니다.


./capture_vmstat.sh

9. stdout ( > )의 경로를 로그 파일로 재지정하거나 stdout ( >> )을 로그 파일에 덧붙입니다.


./capture_vmstat.sh >> ${LOG_FILE}

10. stderr의 경로를 stdout과 동일한 대상 또는 고유한 파일로 재지정합니다.


./capture_vmstat.sh  >> ${LOG_FILE}  2>&1

- or -

./capture_vmstat.sh  >> ${LOG_FILE}  2>>${ERR_LOG}

11. for-loop를 사용해 목록을 처리합니다.


export LIST=$(ls *sh)
for FILE in ${LIST}
do
	echo "Processing ${FILE}"
	cat ${FILE} | mailx -s "Here is ${FILE}" 
          userA@domain.com
done

Use the while-loop to process the same command 
  repeatedly.
export INTERVAL=20
export COUNT=180

export CTR=0
while [ true ]
do
if [ ${CTR} -ge ${COUNT} ]
	then
		exit
	fi
	# --- do some command here ---
	sleep ${INTERVAL}
	      CTR=$(expr ${CTR} + 1) 
done

참조 자료


UNIX Shell Programming (Hayden Books UNIX System Library)


필자: Stephen G. Kochan, Patrick H. Wood
페이퍼백 - 490페이지
2번째 개정판(1990년 1월)
Hayden Books; ISBN: 067248448X

필자 약력


켄 코트리(Ken Gottry)는 현재 NerveWire, Inc.의 수석 인프라 아키텍트로 근무하고 있으며, 메인프레임에서 데스크탑에 이르는 시스템 부문에서 30년 간 경력을 쌓았습니다. 지난 10년 동안 그는 분산, 다계층 및 웹 기반 시스템의 설계, 구현, 튜닝 업무에 전념해 왔습니다. 또한, 수많은 G2K 업체에 자문을 제공하는 성능 엔지니어로서 웹 서버, 애플리케이션 서버, Solaris상에서 실행되는 JVM을 평가하고 튜닝했습니다.

켄의 기술 자료는 썬의 개발자 웹 사이트에 게재되었던 내용이며, 최근 SysAdmin 매거진에서 Solaris 성능 튜닝에 대한 기술 자료도 발표됐습니다.

반응형

Bash를 이용한 쉘 스크립팅 완전 가이드

Mendel Cooper

Brindlesoft

thegrendel (at) theriver.com



 

차례

Part 1. 소개
1. 왜 쉘 프로그래밍을 해야 하죠?
2. #! 으로 시작하기
2.1. 스크립트 실행하기
2.2. 몸풀기 연습문제(Preliminary Exercises)
Part 2. 기초 단계
3. 종료와 종료 상태(Exit and Exit Status)
4. 특수 문자
5. 변수와 매개변수 소개
5.1. 변수 치환(Variable Substitution)
5.2. 변수 할당(Variable Assignment)
5.3. Bash 변수는 타입이 없다(untyped)
5.4. 특수한 변수 타입
6. 쿼우팅(quoting)
7. 테스트
7.1. 테스트(Test Constructs)
7.2. 파일 테스트 연산자
7.3. 비교 연산자(이진)
7.4. 중첩된 if/then 조건 테스트
7.5. 여러분이 테스트문을 얼마나 이해했는지 테스트 해보기
8. 연산자 이야기(Operations and Related Topics)
8.1. 연산자(Operators)
8.2. 숫자 상수(Numerical Constants)
Part 3. 중급 단계(Beyond the Basics)
9. 변수 재검토(Variables Revisited)
9.1. 내부 변수(Internal Variables)
9.2. 문자열 조작
9.3. 매개변수 치환(Parameter Substitution)
9.4. 변수 타입 지정: declaretypeset
9.5. 변수 간접 참조
9.6. $RANDOM: 랜덤한 정수 만들기
9.7. 이중소괄호(The Double Parentheses Construct)
10. 루프와 분기(Loops and Branches)
10.1. 루프
10.2. 중첩된 루프
10.3. 루프 제어
10.4. 테스트와 분기(Testing and Branching)
11. 내부 명령어(Internal Commands and Builtins)
11.1. 작업 제어 명령어
12. 외부 필터, 프로그램, 명령어
12.1. 기본 명령어
12.2. 복잡한 명령어
12.3. 시간/날짜 명령어
12.4. 텍스트 처리 명령어
12.5. 파일, 아카이브(archive) 명령어
12.6. 통신 명령어
12.7. 터미널 제어 명령어
12.8. 수학용 명령어
12.9. 기타 명령어
13. 시스템과 관리자용 명령어
14. 명령어 치환(Command Substitution)
15. 산술 확장(Arithmetic Expansion)
16. I/O 재지향
16.1. exec 쓰기
16.2. 코드 블럭 재지향
16.3. 응용
17. Here Documents
18. 쉬어가기
Part 4. 고급 주제들(Advanced Topics)
19. 정규 표현식(Regular Expressions)
19.1. 정규 표현식의 간략한 소개
19.2. Globbing
20. 서브쉘(Subshells)
21. 제한된 쉘(Restricted Shells)
22. 프로세스 치환(Process Substitution)
23. 함수
23.1. 복잡 함수와 함수의 복잡성(Complex Functions and Function Complexities)
23.2. 지역 변수와 재귀 함수(Local Variables and Recursion)
24. 별칭(Aliases)
25. 리스트(List Constructs)
26. 배열
27. 파일들
28. /dev 와 /proc
28.1. /dev
28.2. /proc
29. 제로와 널(Of Zeros and Nulls)
30. 디버깅
31. 옵션
32. 몇 가지 지저분한 것들(Gotchas)
33. 스타일 있게 스크립트 짜기
33.1. 비공식 쉘 스크립팅 스타일시트
34. 자질구레한 것들
34.1. 대화(interactive)형 모드와 비대화(non-interactive)형 모드 쉘과 스크립트
34.2. 쉘 래퍼(Shell Wrappers)
34.3. 테스트와 비교: 다른 방법
34.4. 최적화
34.5. 팁 모음(Assorted Tips)
34.6. 괴상한 것(Oddities)
34.7. 이식성 문제(Portability Issues)
34.8. 윈도우즈에서의 쉘 스크립팅
35. Bash, 버전 2
36. 후기(Endnotes)
36.1. 저자 후기(Author's Note)
36.2. 저자에 대해서
36.3. 이 책을 만드는데 쓴 도구들
36.3.1. 하드웨어
36.3.2. 소프트웨어와 프린트웨어
36.4. 크레딧
서지사항
A. 여러분들이 보내준 스크립트들(Contributed Scripts)
B. Sed 와 Awk 에 대한 간단한 입문서
B.1. Sed
B.2. Awk
C. 특별한 의미를 갖는 종료 코드
D. I/O와 I/O 재지향에 대한 자세한 소개
E. 지역화(Localization)
F. 샘플 .bashrc 파일
G. 도스(DOS) 배치 파일을 쉘 스크립트로 변환
H. 연습문제
I. Copyright
예 목록
2-1. cleanup: /var/log 에 있는 로그 파일들을 청소하는 스크립트
2-2. cleanup: 위 스크립트의 향상되고 일반화된 버전.
3-1. 종료/종료 상태
3-2. !으로 조건을 부정하기
4-1. 코드 블럭과 I/O 재지향
4-2. 코드 블럭의 결과를 파일로 저장하기
4-3. 최근 하루동안 변경된 파일들을 백업하기
5-1. 변수 할당과 치환
5-2. 평범한 변수 할당
5-3. 평범하고 재미있는 변수 할당
5-4. 정수? 문자열?
5-5. 위치 매개변수
5-6. wh, whois 도메인 네임 룩업
5-7. shift 쓰기
6-1. 이상한 변수를 에코하기
6-2. 이스케이프된 문자들
7-1. 무엇이 참인가?
7-2. [ ]test 의 동일함
7-3. (( ))로 산술식 테스트 하기
7-4. 산술 비교와 문자열 비교
7-5. 문자열이 인지 테스트 하기
7-6. zmost
8-1. 산술 연산자 쓰기
8-2. && 와 || 를 쓴 복합 조건 테스트
8-3. 숫자 상수 표기법:
9-1. $IFS 와 빈 칸
9-2. 타임 아웃 처리 입력
9-3. 타임 아웃 처리 입력, 한 번 더
9-4. 내가 루트인가?
9-5. arglist: $* 과 $@ 로 인자를 나열하기
9-6. 일관성 없는 $*$@의 동작
9-7. $IFS 가 비어 있을 때 $*$@
9-8. 밑줄 변수(underscore variable)
9-9. 그래픽 파일을 다른 포맷 확장자로 이름을 바꾸면서 변환
9-10. 매개변수 치환과 : 쓰기
9-11. 변수의 길이
9-12. 매개변수 치환에서의 패턴 매칭
9-13. 파일 확장자 바꾸기:
9-14. 임의의 문자열을 파싱하기 위해 패턴 매칭 사용하기
9-15. 문자열의 접두, 접미어에서 일치하는 패턴 찾기
9-16. declare를 써서 변수 타입 지정하기
9-17. 간접 참조
9-18. awk에게 간접 참조를 넘기기
9-19. 랜덤한 숫자 만들기
9-20. RANDOM 으로 주사위를 던지기
9-21. RANDOM 에 seed를 다시 지정해 주기
9-22. C 형태의 변수 조작
10-1. 간단한 for 루프
10-2. 각 [list] 항목이 인자를 두 개씩 갖는 for
10-3. Fileinfo: 변수에 들어 있는 파일 목록에 대해 동작
10-4. for 문에서 파일 조작하기
10-5. in [list]가 빠진 for
10-6. for 문의 [list]에 명령어 치환 쓰기
10-7. 이진 파일에 grep 걸기
10-8. 특정 디렉토리의 모든 바이너리 파일에 대해 원저작자(authorship)를 확인 하기
10-9. 디렉토리에 들어 있는 심볼릭 링크들을 나열하기
10-10. 디렉토리에 들어 있는 심볼릭 링크들을 파일로 저장하기
10-11. C 형태의 for 루프
10-12. 배치 모드로 efax 사용하기
10-13. 간단한 while 루프
10-14. 다른 while 루프
10-15. 다중 조건 while 루프
10-16. C 형태의 문법을 쓰는 while 루프
10-17. until 루프
10-18. 중첩된 루프
10-19. 루프에서 breakcontinue의 영향
10-20. 여러 단계의 루프에서 탈출하기
10-21. 더 상위 루프 레벨에서 계속하기(continue)
10-22. case 쓰기
10-23. case로 메뉴 만들기
10-24. case용 변수를 만들기 위해서 명령어 치환 쓰기
10-25. 간단한 문자열 매칭
10-26. 입력이 알파벳인지 확인하기
10-27. select로 메뉴 만들기
10-28. 함수에서 select를 써서 메뉴 만들기
11-1. printf가 실제로 쓰이는 예제
11-2. read로 변수 할당하기
11-3. read로 여러줄의 입력 넣기
11-4. read파일 재지향과 같이 쓰기
11-5. 현재 작업 디렉토리 변경하기
11-6. let으로 몇 가지 산술 연산을 하기.
11-7. eval의 효과 보여주기
11-8. 강제로 로그 아웃 시키기
11-9. "rot13" 버전
11-10. 위치 매개변수와 set 쓰기
11-11. 변수를 "언셋"(unset) 하기
11-12. export를 써서, 내장된 awk 스크립트에 변수를 전달하기
11-13. getopts로 스크립트로 넘어온 옵션과 인자 읽기
11-14. 데이타 파일 "포함하기"
11-15. exec 효과
11-16. 작업을 계속 해 나가기 전에 프로세스가 끝나길 기다리기
12-1. CDR 디스크를 구울 때 ls로 목차 만들기
12-2. Badname, 파일 이름에 일반적이지 않은 문자나 공백 문자를 포함하는 파일을 지우기.
12-3. inode 로 파일을 지우기
12-4. 시스템 로그 모니터링용 xargs 로그 파일
12-5. copydir. xargs로 현재 디렉토리를 다른 곳으로 복사하기
12-6. expr 쓰기
12-7. date 쓰기
12-8. 스크립트에서 두 파일을 비교하기 위해 cmp 쓰기.
12-9. 낱말 빈도수 분석
12-10. 10자리 랜덤한 숫자 만들기
12-11. tail로 시스템 로그를 모니터하기
12-12. 스크립트에서 "grep"을 에뮬레이트 하기
12-13. 목록에 들어 있는 낱말들의 유효성 확인하기
12-14. toupper: 파일 내용을 모두 대문자로 바꿈.
12-15. lowercase: 현재 디렉토리의 모든 파일명을 소문자로 바꿈.
12-16. du: 도스용 텍스트 파일을 UNIX용으로 변환.
12-17. rot13: 초허접(ultra-weak) 암호화, rot13.
12-18. "Crypto-Quote" 퍼즐 만들기
12-19. 파일 목록 형식화.
12-20. column 으로 디렉토리 목록을 형식화 하기
12-21. nl: 자기 자신에게 번호를 붙이는 스크립트.
12-22. cpio로 디렉토리 트리 옮기기
12-23. rpm 아카이브 풀기
12-24. C 소스에서 주석을 제거하기
12-25. /usr/X11R6/bin 둘러보기
12-26. basenamedirname
12-27. 인코드된 파일을 uudecode하기
12-28. 저당에 대한 월 상환액(Monthly Payment on a Mortgage)
12-29. 진법 변환(Base Conversion)
12-30. 다른 방법으로 bc 실행
12-31. seq로 루프에 인자를 만들어 넣기
12-32. 키보드 입력을 갈무리하기
12-33. 파일을 안전하게 지우기
12-34. m4 쓰기
13-1. 지움 글자(erase character) 세팅하기
13-2. 비밀스런 비밀번호: 터미널 에코 끄기
13-3. 키누름 알아내기
13-4. pidof 로 프로세스를 죽이기
13-5. CD 이미지 확인하기
13-6. 한 파일에서 한번에 파일 시스템 만들기
13-7. 새 하드 드라이브 추가하기
13-8. killall, /etc/rc .d/init.d 에서 인용
16-1. exec으로 표준입력을 재지향 하기
16-2. 재지향된 while 루프
16-3. 다른 형태의 재지향된 while 루프
16-4. 재지향된 until 루프
16-5. 재지향된 for 루프
16-6. 재지향된 for 루프(표준입력, 표준출력 모두 재지향됨)
16-7. 재지향된 if/then 테스트
16-8. 이벤트 로깅하기
17-1. dummyfile: 두 줄짜리 더미 파일 만들기
17-2. broadcast: 로그인 해 있는 모든 사람들에게 메세지 보내기
17-3. cat으로 여러 줄의 메세지 만들기
17-4. 탭이 지워진 여러 줄의 메세지
17-5. Here document에서 매개변수 치환하기
17-6. 매개변수 치환 끄기
17-7. upload: "Sunsite" incoming 디렉토리에 파일 한 쌍을 업로드
17-8. "아무개"(anonymous) Here Document
20-1. 서브쉘에서 변수의 통용 범위(variable scope)
20-2. 사용자 프로파일 보기
20-3. 프로세스를 서브쉘에서 병렬로 돌리기
21-1. 제한된 모드로 스크립트 돌리기
23-1. 간단한 함수
23-2. 매개변수를 받는 함수
23-3. 두 숫자중 큰 수 찾기
23-4. 숫자를 로마 숫자로 바꾸기
23-5. 함수에서 큰 값을 리턴하는지 테스트하기
23-6. 큰 두 정수 비교하기
23-7. 사용자 계정 이름에서 실제 이름을 알아내기
23-8. 지역 변수의 영역(Local variable visibility)
23-9. 지역 변수를 쓴 재귀 함수
24-1. 스크립트에서 쓰이는 별칭(alias)
24-2. unalias: 별칭을 설정, 해제하기
25-1. "and list"를 써서 명령어줄 인자 확인하기
25-2. "and list"를 써서 명령어줄 인자를 확인하는 다른 방법
25-3. "or lists""and list"를 같이 쓰기
26-1. 간단한 배열 사용법
26-2. 배열의 특별한 특성 몇 가지
26-3. 빈 배열과 빈 원소
26-4. 아주 오래된 친구: 버블 정렬(Bubble Sort)
26-5. 복잡한 배열 어플리케이션: 에라토스테네스의 체(Sieve of Erastosthenes)
26-6. 복잡한 배열 어플리케이션: 기묘한 수학 급수 탐색(Exploring a weird mathematical series)
26-7. 2차원 배열을 흉내낸 다음, 기울이기(tilting it)
28-1. 특정 PID와 관련있는 프로세스 찾기
28-2. 온라인 연결 상태
29-1. 쿠키 항아리를 숨기기
29-2. /dev/zero로 스왑 파일 세팅하기
29-3. 램디스크 만들기
30-1. 버그 있는 스크립트
30-2. test24, 버그가 있는 다른 스크립트
30-3. "assert"로 조건을 테스트하기
30-4. exit 잡아채기(Trapping at exit)
30-5. Control-C 가 눌렸을 때 깨끗이 청소하기
30-6. 변수 추적하기
32-1. 서브쉘 함정(Subshell Pitfalls)
34-1. 쉘 래퍼(shell wrapper)
34-2. 조금 복잡한 쉘 래퍼(shell wapper)
34-3. awk 스크립트 쉘 래퍼(shell wrapper)
34-4. Bash 스크립트에 내장된 펄
34-5. 하나로 묶인 Bash 스크립트와 펄 스크립트
34-6. 자신을 재귀적으로 부르는 스크립트
35-1. 문자열 확장
35-2. 간접 변수 참조 - 새로운 방법
35-3. 배열과 약간의 트릭을 써서 한 벌의 카드를 4명에게 랜덤하게 돌리기
A-1. manview: 포맷된 맨 페이지를 보는 스크립트
A-2. mailformat: 이메일 메세지를 포맷해서 보기
A-3. rn: 간단한 파일이름 변경 유틸리티
A-4. encryptedpw: 로컬에 암호화 되어 있는 비밀번호로 ftp 사이트에 파일을 업로드하는 스크립트
A-5. copy-cd: 데이타 CD를 복사하는 스크립트
A-6. days-between: 두 날짜 사이의 차이를 계산해 주는 스크립트
A-7. behead: 메일과 뉴스 메세지 헤더를 제거해 주는 스크립트
A-8. ftpget: ftp에서 파일을 다운로드 해 주는 스크립트
A-9. password: 8 글자짜리 랜덤한 비밀번호 생성 스크립트
A-10. fifo: 네임드 파이프를 써서 매일 백업해 주는 스크립트
A-11. 나머지 연산자로 소수 생성하기
A-12. tree: 디렉토리 구조를 트리 형태로 보여주는 스크립트
A-13. 문자열 함수들: C 형태의 문자열 함수
A-14. 객체 지향 데이타 베이스
F-1. 샘플 .bashrc 파일
G-1. VIEWDATA.BAT: 도스용 배치 파일
G-2. viewdata.sh: VIEWDATA.BAT 의 스크립트 버전

출처 : http://wiki.kldp.org/HOWTO//html/Adv-Bash-Scr-HOWTO/index.html

+ Recent posts