UNIX 쉘은 본질적으로 사용자, 커널 및 시스템 하드웨어 사이의 API이다. 쉘은 모든 UNIX 또는 Linux 시스템에서 매우 중요하고 올바른 시스템 관리 및 보안을 배워야 할 가장 중요한 요소 중 하나이다. 일반적으로 CLI를 기반으로 하는 쉘은 말 그대로 시스템을 만들거나 깨뜨릴 수 있다. 본 기사에서 설명하는 오픈 소스 bash 쉘은 사용 가능한 쉘 중에서도 가장 강력하고 실용적이며 확장성이 뛰어난 쉘 중 하나이다. 이 기사에서는 bash 스크립트 작성의 기초, 스크립트의 일반적 사용법, 이를 이용해 거의 완전한 쉘 스크립트를 작성하기 위한 메소드를 학습한다.
Bash(Bourne Again Shell)는 1987년에서 처음으로 등장했는데, 그때 이 쉘은 GNU 프로젝트로 작성되었고 많은 Linux 배포 버전에서 이 쉘이 신속히 채택되었다. 현재, bash의 다양한 버전을 무료로 사용할 수 있다.
Bash의 긍정적인 측면 중 하나는 이 쉘의 내장 보안 기능이다. Bash는 사용자가 입력한 내용을 정확히 기록하여 유지하고 사용자의 홈 디렉토리 내에 있는 .bash_history라는 숨겨진 파일에 그 내용을 쓴다. 따라서 bash를 구현하면 시스템 사용자를 훨씬 더 철저하고 손쉽게 추적할 수 있다. 사실, 많은 Linux 시스템의 경우 bash는 기본 쉘 환경으로 미리 설치되어 있는 것이 일반적이다.
Bash 명령 구문과 키워드는 Korn 쉘(ksh) 및 C 쉘(csh)의 아키텍처와 기술적 세부사항을 바탕으로 선택 및 개선되었다. 또한,
bash의 구문은 다른 쉘에는 없는 다양한 확장 기능이 있다.
Bash에서는 다른 쉘에서보다 정수 계산이 더 효율적으로 완료되고, bash는 이전 쉘들에 비해 표준 출력(stdout)과 표준 오류(stderr)를 더 쉽게 리디렉션할 수
있다.
Bash는 쉘 내에서 사용자의 능력을 간단히 결정할 수 있는 명령 목록으로 한정할 수 있는 제한 시작 모드를 가지고 있어, 보안 환경에도 이상적이다. 수정이 가능하므로, 사용자 고유의 bash 쉘 로그인 제어 파일(즉, .bashrc, .bash_profile, .bash_logout 및 .profile과 같은 숨겨진 파일) 세트를 편집하여 로그인 쉘을 사용자 정의할 수 있다.
효과적인 bash 쉘 스크립트를 작성하려면 쉘 내에서 수행되는 탐색 및 일상적으로 반복되는 작업을 위한 기본적인 bash 명령 세트를 마스터해야 한다.
로그인할 때, 사용자에게는 보통 실행되는 두 개의 개인 파일(.bash_profile 및 .bashrc)뿐 아니라, 글로벌 파일 하나가 있다. 그림 1은 일반적인 프로세스 플로우를 나타낸 것이다.
그림 1. Bash 쉘 로그인 프로세스
이제, 일반적인 Linux 사용자의 .bash_profile(목록 1) 및 .bashrc(목록 2) 스크립트를 살펴보자. 이 스크립트는 사용자의 홈 디렉토리에서 로드된다.
목록 1. 일반적인 .bash_profile 파일
[fred.smythe@server01 ~]$ cat .bash_profile
# .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
# User specific environment and startup programs
export JAVA_HOME=/usr/java/default
export PATH=$JAVA_HOME/bin:$PATH
PATH=$PATH:$HOME/bin
export PATH
|
목록 2의 .bashrc 파일에서 몇몇 사용자 별명이 구성되고 글로벌 bashrc 파일이 있는 경우 이 파일이 로드된다.
목록 2. 일반적인 .bashrc 파일
[fred.smythe@server01 ~]$ cat .bashrc
# .bashrc
# User specific aliases and functions
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
alias tc6='cd /opt/tomcat/6.0.13'
alias conf6='cd /opt/tomcat/6.0.13/conf'
alias bin6='cd /opt/tomcat/6.0.13/bin'
alias scr='cd /opt/tomcat/scripts'
alias reports='cd /opt/tomcat/reports'
alias temp6='cd /opt/tomcat/6.0.13/template'
# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
|
시스템 레벨 프로세스 init은 getty 또는 agetty 프로세스를 생성한다. 그러면 이 프로세스가 /bin/login 프로그램을 호출하고, 이 프로그램에서는 친숙한 UNIX 또는 Linux 로그인 프롬프트가 표시된다. 사용자는 로그인 프로그램에서 새 bash 쉘을 생성하는 지점에서 시스템 액세스를 위한 로그인 프로세스를 계속 진행한다. Bash 쉘 사용자가 로그인할 때 이런 순서로 다음 이벤트가 발생한다. 글로벌 프로파일을 읽고 사용자의 개별 bash 프로파일을 읽은 다음, 일반적으로 별명을 설정하고 추가 함수를 정의하고 사용자의 개별 환경 변수를 정의하는 사용자의 개인 '.bashrc'를 읽는다. 마지막으로, bash 사용자에게 bash 쉘 프롬프트를 표시하고 motd 파일을 읽어 표시한다. 아래의 그림 2는 도해로 나타낸 예제이다.
그림 2. Bash 쉘 로그인 프로세스 세부사항
이제 사용자는 bash 쉘 환경 $PATH 변수 내에서 프로그램의 표준 명령 세트를 사용할 수 있다. 사용자의 $PATH에 명령이 없지만 사용자가 해당 명령을 실행할 권한이 있는 경우, 목록 3에 표시된 것처럼 전체 경로를 사용해야 한다.
목록 3. Bash 쉘 내에서 발생하는 $PATH 문제의 예
[fred.smythe@server01 ~]$ ifconfig -a -bash: ifconfig: command not found [fred.smythe@server01 ~]$ which ifconfig /usr/bin/which: no ifconfig in (/usr/local/bin:/bin:/usr/bin:/home/fred.smythe/bin) |
사용자가 정의한 PATH 변수에 2진 프로그램 ifconfig가 없을 때 이 문제가 발생한다. 하지만, 명령의 전체 경로를 알고 있으면
목록 4에 표시된 것처럼 명령을 실행할 수 있다.
목록 4. 명령의 전체 경로를 사용하여 bash 쉘의 $PATH 문제 극복
[fred.smythe@server01 ~]$ /sbin/ifconfig -a
eth0 Link encap:Ethernet HWaddr 00:50:56:96:2E:B3
inet addr:10.14.33.60 Bcast:10.14.33.255 Mask:255.255.255.0
|
목록 5는 별명을 사용하여 $PATH 문제를 극복하는 방법을 나타낸 것이다. Bash 스크립트 내에서 의도한 스크립트를 실행할 사람이 누군지에 따라 전체 경로로 명령을 실행하고 싶을 수도 있다.
목록 5. 별명을 설정하여 bash 쉘의 $PATH 문제 극복
[fred.smythe@server01 ~]$ alias ifconfig='/sbin/ifconfig'
[fred.smythe@server01 ~]$ ifconfig
eth0 Link encap:Ethernet HWaddr 00:50:56:96:2E:B3
inet addr:10.14.33.60 Bcast:10.14.33.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
|
어떤 명령이나 bash 쉘 자체가 어떤 작업을 수행하기 위해 새 쉘 하위 프로세스를 시작하거나 생성할 때, 이를 분기라고 한다. 이 새 프로세스(하위
프로세스)가 실행 중일 때, 상위 프로세스도 여전히 실행되고 있을 것이다. 상위 프로세스를 종료한 후 하위 프로세스를 시작해야 하는 경우 하위 프로세스는
없어진 프로세스가 될 수 있고, 이는 보통 프로세스 또는 애플리케이션이 정지되는 결과를 초래하는 좀비 프로세스라고도 한다. 따라서 이 정지 프로세스가
비정상적으로 강제 종료 또는 종료되어야 한다. 상위 프로세스는 하위 프로세스의 프로세스 ID에 액세스할 수 있고 그에 따라 프로세스 ID로 인수를 전달할 수 있지만,
그 반대로는 되지 않는다. 쉘 스크립트 프로세스가 종료되거나 상위 프로세스로 리턴될 때, 종료 코드는 0이어야 한다. 그 이외의
종료 코드인 경우 프로세스에 오류나 문제점이 있는 일이 많았다.
목록 6에는 마지막으로 실행된 명령의 종료 코드인 echo
$?가 표시되어 있다.
목록 6. 종료 코드 예제
# ls -ld /tmp drwxrwxrwt 5 root root 4096 Aug 19 19:45 /tmp [root@server01 ~]# echo $? 0 // Good command return of 0. [root@server01 ~]# ls -l /junk ls: /junk: No such file or directory [root@server01 ~]# echo $? 2 // Something went wrong, there was an error, so return 2. |
목록 7은 bash 환경 내에 표시된 상위 및 하위 프로세스를 그림으로 설명한 것이다.
목록 7. Bash 환경에서 상위 및 하위 프로세스의 예
[root@server02 htdocs]# ps -ef | grep httpd UID PID PPID C STIME TTY TIME CMD root 8495 1 0 Jul26 ? 00:00:03 /usr/sbin/httpd -k start apache 9254 8495 0 Aug15 ? 00:00:00 /usr/sbin/httpd -k start |
env 명령을 입력하면 사용자 이름 및 tty(터미널) 정보, $PATH 값 및 현재 작업 디렉토리($PWD)를 포함하여,
현재 bash 쉘 기본 환경 변수 중 어떤 것이 설정되어 있는지 확인할 수 있다. 목록 8을 살펴보자.
목록 8. Bash 환경의 예
[fred.smythe@server01 ~]$ env HOSTNAME=server01 TERM=screen SHELL=/bin/bash HISTSIZE=1000 SSH_CLIENT=10.12.132.3 56513 22 SSH_TTY=/dev/pts/0 USER=fred.smythe LS_COLORS=no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:bd=40;33;01:cd=40;33;01:or=01;05 ;37;41:mi=01;05;37;41:ex=01;32:*.cmd=01;32:*.exe=01;32:*.com=01;32:*.btm=01;32:*.bat=01;32 :*.sh=01;32:*.csh=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip= 01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.bz=01;31:*.tz=01;31:*.rpm=01;31:*.cpio= 01;31:*.jpg=01;35:*.gif=01;35:*.bmp=01;35:*.xbm=01;35:*.xpm=01;35:*.png=01;35:*.tif=01;35: MAIL=/var/spool/mail/fred.smythe PATH=/usr/local/bin:/bin:/usr/bin:/home/fred.smythe/bin INPUTRC=/etc/inputrc PWD=/home/fred.smythe LANG=en_US.UTF-8 SHLVL=1 HOME=/home/fred.smythe LOGNAME=fred.smythe SSH_CONNECTION=10.14.43.183 56513 10.14.43.43 22 LESSOPEN=|/usr/bin/lesspipe.sh %s G_BROKEN_FILENAMES=1 _=/bin/env |
목록 9에 표시된 bash 명령을 사용하여 Linux 파일 시스템을 탐색할 수 있다.
목록 9: Bash 환경에서의 탐색
[fred.smythe@server01 ~]$ ls -l
total 0
[fred.smythe@server01 ~]$ cd /tmp
[fred.smythe@server01 tmp]$ df -ha .
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/vg_root-lv_tmp
2.0G 68M 1.8G 4% /tmp
|
이 목록에서는 명령을 한 번에 하나씩 사용했다. 그러나 목록 10에 표시된 것처럼, 세미콜론(;) 구분
기호를 사용하여 이들 명령을 함께 실행할 수 있었을 것이다.
목록 10: Bash에서의 순차적 명령 실행
[fred.smythe@server01 tmp]$ ls -l ;cd /tmp;df -ha .
total 40
-rw-r----- 1 root root 1748 May 22 2009 index.html
-rw-r----- 1 root root 786 Aug 17 04:59 index.jsp
drwx------ 2 root root 16384 Jul 15 2009 lost+found
drwx------ 2 root root 4096 Aug 9 21:04 vmware-root
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/vg_root-lv_tmp
2.0G 68M 1.8G 4% /tmp
[fred.smythe@server01 tmp]$
|
Bash 명령행에서는 명령 완성 기능이 제공되므로 일상적 작업에 필요한 입력 시간이 단축된다. 즉, 어떤 명령의 시작 부분만 입력한 다음 Tab 키를 누르면 된다. 권한 제한 때문에 명령이나 파일에 액세스할 수 없는 경우에는 명령 완성 기능이 자동으로 실행되지 않는다.
Bash에서는 다음과 같이 몇 가지 형식의 도움말을 제공한다.
man(아래의 목록 11에 표시됨):
목록 11. Bash에서 매뉴얼 페이지의 예[fred.smythe@server01 tmp]$ man perl PERL(1) Perl Programmers Reference Guide PERL(1) NAME perl - Practical Extraction and Report Language SYNOPSIS perl [ -sTtuUWX ] [ -hv ] [ -V[:configvar] ] -cw ] [ -d[t][:debugger] ] [ -D [num- ber/list] ] [ -pna ] [ -Fpattern ] [ -l[octal] ] [ -0[octal/hexadecimal] ] [ -Idir ] [ -m[-]module ] [ -M[-] module... ] [ -f ] [ -C [number/list] ] [ -P ] [ -S ] [ -x[dir] ] [ -i[extension] ] [ -e command ] [ -- ] [ program- file ] [ argument ]...
whatis(아래의 목록 12에 표시됨):
목록 12. Bash에서 whatis 명령의 예제[fred.smythe@server01 tmp]$ whatis perl perl (1) - Practical Extraction and Report Language perl (rpm) - The Perl programming language
apropos(아래의 목록 13에 표시됨):
목록 13. Bash의 Apropos 예제[root@server01 ~]# apropos perl | more B (3pm) - The Perl Compiler B::Asmdata (3pm) - Autogenerated data about Perl ops, used to generate bytecode B::Assembler (3pm) - Assemble Perl bytecode
which(아래의 목록 14에 표시됨):
목록 14. Bash의 which 명령[root@server01 ~]# which perl /usr/bin/perl
Bash 쉘에는 두 가지 유형의 명령, 즉 내부의 builtins와 보통 자체 포함된 2진 프로그램 파일인 외부 프로그램(또는 외부 필터 및 명령)이
포함된다. 목록 15는 alias 명령을 사용하여 bash에서 내장 명령을 찾는 방법을 나타낸 것이다.
목록 15. Bash 내부의 내장 명령 찾기
[root@server01 ~]# man -k builtins| more . [builtins] (1) - bash built-in commands, see bash(1) : [builtins] (1) - bash built-in commands, see bash(1) [ [builtins] (1) - bash built-in commands, see bash(1) alias [builtins] (1) - bash built-in commands, see bash(1) bash [builtins] (1) - bash built-in commands, see bash(1) bg [builtins] (1) - bash built-in commands, see bash(1) bind [builtins] (1) - bash built-in commands, see bash(1) break [builtins] (1) - bash built-in commands, see bash(1) builtin [builtins] (1) - bash built-in commands, see bash(1) |
Bash에서 특정 명령을 식별하려면 목록 16에 표시된 것처럼 type 명령을 사용한다.
목록 16. type 명령을 사용하여 내장 명령 찾기
[root@apache-02 htdocs]# type lsof lsof is /usr/sbin/lsof [root@apache-02 htdocs]# type alias alias is a shell builtin |
목록 17에서는 외부 명령인 lsof의 예제를 제시한다. 이 명령은 실제로는 Linux 파일 시스템에 있는
2진 파일로, 같은 이름의 패키지를 통해 설치된다.
목록 17. Bash 내에서 외부 명령 세부사항 찾기
[root@server01 ~]# which lsof /usr/sbin/lsof [root@server01 ~]# rpm -qa lsof-4.78-3.i386 [root@server01 ~]# rpm -qa lsof lsof-4.78-3.i386 |
Bash 쉘의 가장 강력한 기능 중 하나는 작동 중에 명령행 스크립트를 사용할 수 있다는 점이다. 목록 18에서 쉘 변수를 설정하고 변수의 값을 테스트한 후 값이 0보다 큰 경우 프로그램에 따라 자동으로 다른 명령을 실행하는 예제를 고찰해보자.
목록 18. Bash로 도중에 스크립트 작성
[fred.smythe@server01 ~]$ DEBUG=1 [fred.smythe@server01 ~]$ test $DEBUG -gt 0 && echo "Debug turned on" Debug turned on |
다음은 도중에 작성된 for 루프의 예제이다(목록 19에 표시됨). 여기서 쉘 프롬프트에 대화식으로 입력하고 있다는 점에 유의하자.
각각의 >에서 대화식 쉘 스크립트의 다음 행을 입력한다.
목록 19. Bash 내에서 도중에 작성된 for 루프
$ for SVR in 1 2 3 > do > echo server0$SVR.example.com > done server01.example.com server02.example.com server03.example.com |
또는 세미콜론으로 구분되는 순차적 명령으로 이 코드를 실행할 수 있었을 것이다.
for 명령은 프로그램이 아니지만, 키워드라고 알려진 일종의 특수한 내장 명령이다. Linux에서 bash의 평균적인
배포에서 키워드 목록이 목록 20에 표시되어 있다.
목록 20. Bash 내의 키워드
true, false, test, case, esac, break, continue, eval, exec, exit, export, readonly, return, set, shift, source, time, trap, unset, time, date, do, done, if, fi, else, elif, function, for, in, then, until, while, select |
쉘 변수의 이름을 선택할 때 이런 키워드 또는 bash 쉘 예약어로 알려진 것은 피해야 한다.
Bash 쉘을 통해 Linux 또는 UNIX 시스템에서 표준 입력, 표준 출력 및 표준 오류를 리디렉션할 수 있다. 목록 21의 예제를 고찰해보자.
목록 21. Bash 내에서 명령을 함께 파이프 처리
$ USER="fred.smythe"
$ echo -n "User $USER home is: " && cat /etc/passwd | grep $USER | awk -F: '{print $6}'
User fred.smythe home is: /home/fred.smythe
$
# Re-direction of standard output (>) of the date command to a file :
[root@server01 ~]# date > /tmp/today.txt
[root@server01 ~]# cat /tmp/today.txt
Thu Aug 19 19:38:33 UTC 2010
# Re-direction of standard input (<) to standard output (>) …
[root@server01 ~]# cat < /tmp/today.txt > /tmp/today.txt.backup
[root@server01 ~]# cat /tmp/today.txt.backup
Thu Aug 19 19:38:33 UTC 2010
|
복합 명령행에서는 표준 입력, 표준 출력 및 표준 오류 리디렉션 및/또는 파이프의 인스턴스를 여러 개 사용하고 결합하여 높은 수준의 정밀도와 정확도로 어려운 연산을 수행할 수 있다. 목록 22에 그에 대한 예제가 나와 있다.
목록 22. Bash 내에서 리디렉션의 예제
# command1 < input_file1.txt > output_file1.txt # command1 | command2 | command3 > output_file.log |
예를 들어, 복합하게 결합된 명령행을 사용하여 찾은 모든 압축 오류 로그 내에서 검색하고 오류 수를 계수함으로써 Apache 권한으로 정의된 오류를 찾을 수 있다.
$ find ./ -name 'error_log.*.gz' | xargs zcat | grep 'Permission denied'| wc -l 3 |
프로덕션 환경에 유효하거나 엔터프라이즈 레벨의 스크립트를 작성하기 위해 염두에 두어야 할 몇 가지 핵심 포인트는 다음과 같다.
- 항상 짧은 표제로 스크립트에 주석을 추가한다.
- 훗날에 자신이 작성한 소스 코드를 그렇게 작성한 이유를 쉽게 기억해낼 수 있도록, 코드에 자세한 주석을 추가한다. 스크립트의 첫 행은
#!/bin/bash행이 되어야 한다는 점을 기억하자. - 향후 검사를 위한 날짜와 시간소인을 포함한 로그 파일에 스크립트 동작을 작성한다. 출력 결과를 자세히 설명하고 성공 메시지를 로그에 기록하고
오류 메시지 또는 조건을 분명히 진술한다. 스크립트의 시작 및 중지 시간을 로그에 기록하는 것도 현명한 방법일 것이다. 다음과 같이
tee명령을 사용하면 로그와 표준 출력에 동시에 추가할 수 있다.DATEFMT=`date "+%m/%d/%Y %H:%M:%S"` echo "$DATEFMT: My message" | tee -a /tmp/myscript.log
- 스크립트가 로그 파일에 기록되면, 항상 새 로그 파일을 작성하고 새 로그 파일 이름에 날짜, 시간, 분 및 초까지 정확한 시간 정보를 포함시킨다.
그러면 간단한
find명령으로 스크립트를 실행할 때마다 스크립트의 자체 로그를 손쉽게 회전 및 압축할 수 있다.DATELOG=`date "+%m%d%y"` LOGFILE="/data/maillog/mail_stats.log.$DATELOG" # gzip the old mail_stats logs, older than 7 days find /logs -type f -name "mail_stats.log.????????????" -mtime +7 | xargs gzip -9 # remove the old gzipped mail_stats logs after 30 days find /logs -type f -name "mail_stats.log.*.gz" -mtime +30 -exec rm {} \; # mail_log utility log resets echo "" > /var/log/mail_stats.log.$DATELOG
- 스크립트에 오류 검사 논리를 빌드하고 절대로 어떤 것도 가정하지 않는다. 그렇게 해야 나중에 크게 후회할 일이 생기지 않을 것이다.
- (다른 스크립트에 소싱하는 방법으로) 스크립트 내에서 가능한 곳에 함수 및 쉘 스크립트 라이브러리의 사용법을 포함한다. 그렇게 하면 테스트를 거쳐 검증된 코드를 다시 사용할 수 있으므로, 스크립트 코드를 불필요하게 반복하지 않아도 되고 그에 따른 오류 발생 가능성도 차단할 수 있다.
- 다음과 같이 사용자의 입력 매개변수를 적절히 필터링한다.
NUMPARAMETERS="$#" if [ $NUMPARAMETERS != 3 ];then echo "Insufficient number of parameter passed!” echo “Please run script in format ./myscript.sh $1 $2 $3” exit 2 fi
- 예컨대,
set –x명령으로 스크립트 내에 디버그 모드 또는 함수를 추가하는 방안을 고려한다. - 스크립트 내에서 특정 이벤트에 대해 경고하거나 트랩을 보내기 위한 함수를 추가한다.
SNMP 명령 또는 신호음(
echo x)을 통해 이 작업을 수행한 다음,mail명령으로 이메일 메시지를 보낼 수 있다. - 대화식 메뉴와 같이 사용될 스크립트를 작성할 경우에는 사용자의 환경 쉘과 액세스 권한을 가진 대상 명령을 고려한다. 의문이 드는 경우에는 스크립트 내에서 모든 명령에 대해 전체 경로를 사용한다.
- Bash 쉘 스크립트에 개별적이고 고유한 리턴 코드를 추가한다. 큰 스크립트를 쓸 때 리턴 코드를 정확히 가리키면 오류나 문제점이 발생하고 있는 곳을
손쉽고 정확하게 식별할 수 있을 것이다.
if [ “$ERRCHECK” != “” ];then echo “Error Detected : $ERRCHECK ! Cannot continue, exit $EXITVAL value” exit $EXITVAL fi
- 발생 가능한 모든 조건에 대해 랩 환경에서 스크립트를 철저히 테스트한다. 다른 사용자들도 스크립트에 대한 테스트를 수행하고 의도적으로 스크립트 "중단"을 시도해보도록 한다.
- 작성한 스크립트가 사용자가 제공하거나 데이터 입력 파일에 있는 입력 데이터를 대상으로 작동 중인 경우, 항상 입력 데이터를 철저하게 필터링, 검사 및 유효성 검증을 실시한다. 데이터 목록에서 작동하는 스크립트는 원하는 대로 여러 가지 다른 데이터 목록 세트에서 실행 가능하다.
- 장시간 동안 실행될 스크립트의 경우, 스크립트 내에 제한시간 함수를 두어 n분 후 종료 또는 중지하는 방법을 고려한다.
stty –icannon min 0 time 1800
- 코드를 쉽게 읽을 수 있도록 적절히 들여쓰기를 한다.
특정한 목적으로 작성하는 스크립트의 경우, 스크립트의 목적에 대한 정보를 사용자에게 제공하기 위해 대화식 경고 프롬프트 메시지를 추가할 수도 있다. 예를 들어, 목록 23의 스크립트는 원격 로그를 검색하여 보고서 이메일을 작성한다.
목록 23. 로그를 검색하고 보고서를 작성하기 위한 간단한 bash 스크립트
#!/bin/bash
cd /data01/maillog
MAILSVRS=$(/bin/cat /data01/maillog/mail_servers)
DATELOG=`date "+%m%d%y"`
ALLMAILLOGS="/data01/maillog/all_svr_maillogs.$DATELOG"
MAILREPORT="/data01/maillog/all_svr_maillogs.report.$DATELOG"
MAILADDRESSES=$(/bin/cat /data01/maillog/addresses)
MAILADMINS="admin1@example.com, admin2@example.com"
MAILSTATSLOGALL="/data01/maillog/mailstats.log.all.$DATELOG"
DELDAYSLOGS=10
echo “Mail Report to $ MAILADMINS”
# 1 - Get some logs …
for svr in $MAILSVRS
do
if [ -e "/data01/maillog/$svr.maillog.$DATELOG.gz" ]; then
/bin/rm -f /data01/maillog/$svr.maillog.$DATELOG.gz
fi
done
# 2 - Combine all maillogs from all servers to onefile ($ALLMAILLOGS) ...
/bin/zcat server16.maillog.$DATELOG.gz server17.maillog.$DATELOG.gz
server18.maillog.$DATELOG.gz server19.maillog.$DATELOG.gz >>
$ALLMAILLOGS
# 3 - Run another script
/bin/cat $ALLMAILLOGS | /data01/maillog/mymailstats.pl | tee -a $MAILREPORT
# 4 - Get all of the mailstats logs to one log file to send by Email
/bin/cat /data01/maillog/mailstats.log.server*.$DATELOG > $MAILSTATSLOGALL
# Send the $MAILADMINS the mail reports
/bin/cat $MAILSTATSLOGALL | mail -s "ACME Servers Outbound Mail Servers
mailstats:$DATELOG" $MAILADMINS
|
같은 스크립트이지만 더욱 강력한 기능을 갖춰 개선된 버전을 구하려면 본 기사를 위한 소스 코드를 다운로드한다.
Bash 내에서는 변수를 정의하고 설정하기 위한 여러 가지 유효한 방법이 있다. 목록 24의 스크립트에서 이런 쉘 변수 선언 방법을 보여주는 예제를 제시한다.
목록 24. Bash 변수 정의
$ cat a.sh
#!/bin/bash
A="String Value 1"
B='String Value 2'
C=9675
D=96.75
export E="String Value 3"
# if the variable $F is not ALREADY set to a value, assign "String Value 4" ...
F=${F:="String Value 4"}
echo "A=$A"
echo "B=$B"
echo "C=$C"
echo "D=$D"
echo "E=$E"
echo "F=$F"
exit 0
$ ./a.sh
A=String Value 1
B=String Value 2
C=9675
D=96.75
E=String Value 3
F=String Value 4
|
사용자 입력을 수집하려면 목록 25에 표시된 것처럼 read 문을 사용하고 사용자 입력에 변수를 지정한다.
목록 25. Bash 스크립트에서 사용자 입력 가져오기
$ cat prompt.sh #!/bin/bash echo "Please enter your first and last name:" read ans echo "Hellow $ans , welcome to bash scripting ...!" exit 0 $ ./prompt.sh Please enter your first and last name: Joe Jackson Hello Joe Jackson , welcome to bash scripting ...! |
Bash 쉘 스크립트 내에 있는 함수를 사용하려면 목록 26에 표시된 메소드를 이용한다.
목록 26. Bash 내에서의 함수 구현
$ cat function.sh
#!/bin/bash
funcOne() {
echo "Running function 1, with parameter $1"
date
echo "Current listing /tmp directory, last 3 lines"
ls -lrt /tmp | tail -3
}
echo "Hello World"
funcOne "Server01"
exit 0
$ ./function.sh
Hello World
Running function 1, with parameter Server01
Sun Aug 22 22:43:04 UTC 2010
Current listing /tmp directory, last 3 lines
-rw-r- 1 dsmith progdevel 12749743 Aug 16 20:32 files.tar.gz
drwxr-xr-x 2 jjones progdevel 4096 Aug 16 20:42 ff_hosting_files
-rw-r- 1 rhill heng 1440 Aug 22 19:07 myscript.log
|
또는 목록 27에 표시된 함수를 쓸 수 있다.
목록 27. Bash 내에서 대체 함수 정의
#!/bin/bash
function funcTwo () {
echo "Running function 2, with parameter $1"
date
echo "Current listing /var directory, last 3 lines"
ls -lrt /var | tail -3
}
funcTwo "Server02"
exit 0
$ ./function.sh
Running function 2, with parameter Server02
Sun Aug 22 22:53:16 UTC 2010
Current listing /var directory, last 3 lines
drwxrwxrwt 3 root root 4096 Aug 6 18:22 tmp
drwxr-xr-x 6 root root 4096 Aug 22 04:02 log
drwxrwxr-x 4 root lock 4096 Aug 22 04:22 lock
|
function 키워드는 선택사항이다. 프로그램에서 나중에 함수를 사용하려면 우선 함수를 정의해야 한다는 것이 유일한
규칙이다. 함수의 이름을 호출하고 추가적으로 필요하거나 선택사항인 입력 매개변수를 함수로 전달하기만 하면 함수를 호출할 수 있다.
여러 서버에서 어떤 반복 작업을 수행할 필요가 있다고 해보자. Bash 내에서 for 루프를 사용하면 손쉽게 빠르게 작업을 수행할
수 있다. 코드는 목록 28에 표시되어 있다.
목록 28. 간단한 for 루프의 Bash 스크립트 작성 예제
$SERVERS=”server01 server02 server03 server04 server05” for i in $SERVERS do echo "Removing file from server: $i" ssh $i rm -f /tmp/junk1.txt done |
Bash 내에서 while 루프를 사용하면 지정된 횟수만큼 또는 어떤 조건이 충족될 때까지 어떤 문을 실행할 수 있다.
목록 29의 예제를 참조한다.
목록 29. 간단한 while 루프
LOOPCTR=1 while [ $LOOPCTR -lt 6 ] do echo “Running patch script for server0$LOOPCTR” /data/patch.sh server0$LOOPCTR LOOPCTR=`expr $LOOPCTR + 1` done |
Bash 내에서 case 문을 사용하면 둘 이상의 조건이나 값을 테스트하고 적절한 조치를 취할 수 있다. 반복되는 코드를 줄이거나
단지 더 나은 구조로 작성하기 위해서는 그런 문을 사용하는 것이 때로는 중첩된 for 루프나 if 문보다 나은
방법일 수 있다.
목록 30은 $key 변수의 조건에 따라 함수를 호출하는 간단한 case 문을 나타낸
것이다.
목록 30. Bash에서 case 문을 사용하는 예제
case $key in
q) logit "Quit";
exit;;
1) echo “\tChecking Mail Servers”;
check_mail_servers;;
2) echo "\tChecking Web Servers";
check_web_servers;;
3) echo “\tChecking App Servers;
check_app_servers;;
4) echo “\tChecking Database Servers”;
check_database_servers;;
b) echo "Go Back to Main menu";
MENUFLAG="main";
main_menu;;
*) echo "$key invalid choice";
invalid;;
esac
|
어떤 작업을 신속히 완료해야 하는가? Bash를 사용하면 최신 기업용 웹 위젯도 마치 칼로 두부를 썰듯이 간단하게 만들 수 있으므로, 소요 시간을 대폭 단축할 수 있다. Bash 스크립트가 프로그래밍 언어나 애플리케이션은 아니다. 컴파일러가 필요하지도 않고, 작업할 수 있는 여건이 마련된 소프트웨어 개발 환경이나 특수한 라이브러리가 필요하지도 않다. 그러나 bash 쉘 스크립트는 마치 애플리케이션처럼 작동하고, 심지어는 애플리케이션이 할 수 있는 작업 중 일부를 수행할 수도 있다.
찬성하는 쪽에서는 bash의 다음과 같은 장점을 내세운다.
- 개발 시간이 짧고 코드를 손쉽게 수정할 수 있다. 시간이 흘러도 Bash 스크립트의 기본적인 내용이 거의 바뀌지 않는다.
- 프로그래밍 언어에 따라서는 자주 코딩하거나 구문을 변경해야 할 수도 있는 일부 프로그래밍 언어에 비해 구문이 매우 간단하다.
- 사용자에게 전보다 훨씬 많은 능력을 부여하는 고급 bash 기능들(예: epoch, 함수, 신호 제어, 다양한 확장 기능, 명령 히스토리, 1차원 배열을 사용할 수 있는 방법 등)이 많다.
- Bash 쉘 스크립트 작성을 수행하는 데 필요한 *nix bash 쉘을 크게 벗어나는 것이 없다.
반면, 반대하는 쪽에서는 bash 코드의 다음과 같은 단점을 강조한다.
- Bash 코드는 쉘에서 실행된 후 커널로 전달되는데, 이는 대체로 순수한 시스템 코드인 컴파일된 2진 프로그램보다 느리다. 따라서 bash 쉘 스크립트가 어떤 애플리케이션 디자인 또는 함수에는 바람직하지 않을 수 있다.
- Bash 코드는 일반 텍스트로 되어 있거나 읽기 권한을 가진 사람이면 누구든 쉽사리 볼 수 있는 반면, 컴파일된 2진 프로그램은 읽을 수 없다. 따라서 중요한 데이터를 인코드할 때 감수해야 할 보안 위험이 너무도 크다.
- Bash 코드는 특정한 표준 함수 세트가 없는 반면, 요즘 사용되는 대다수 프로그래밍 언어에는 다양한 프로그래밍 요구에 적합한 고유 또는 기본 제공 함수가 있다.
이제 bash 쉘 스크립트 작성을 위한 기초적인 지식을 습득했으므로, 더 나은 결과를 위해서만 그 지식을 활용하겠다고 다짐해야 한다. 따라서 이제 새로 찾은 crontab, 온라인으로 사용 가능한 놀라운 기능들 그리고 대화식 메뉴로 *nix 세계를 구하자. "bash" 관리자가 되기를 두려워하지 말자!
| 설명 | 이름 | 크기 | 다운로드 방식 |
|---|---|---|---|
| Sample bash shell scripts | Bash_Source.zip | 8KB | HTTP |
교육
-
Bash 구문에 대한 사용자 안내서인 GNU bash 참조 매뉴얼을 확인해보자.
-
Wikipedia에서는 bash 쉘의 정의가 잘 설명되어 있다.
-
어째서 Bash가 다른 것과 견줄 만한지 살펴보려면 쉘 비교를 검토해보자.
-
초보자를 위한 bash basics를 살펴보자.
-
AIX와 UNIX developerWorks 영역:
AIX와 UNIX 영역에서는 AIX 시스템 관리와 UNIX 스킬 확장의 모든 측면과 관련된 풍부한 정보를 제공한다.
-
AIX와 UNIX 입문
AIX와 UNIX 입문 페이지에서 자세한 정보를 볼 수 있다.
-
기술
서점: 다양한 기술 주제와 관련된 서적을 살펴볼 수 있다.
제품 및 기술 얻기
-
Bash를 다운로드하고 bash 쉘로 작업을 시작하기 위해 필요한 자원을 얻을 수 있다.
-
UNIX 및 Linux에 사용 가능한 쉘 목록을 숙독하면 큰 도움이 될 것이다.
토론
-
developerWorks 블로그: 블로그를 읽어 보고
developerWorks community에 참여하자.
- Twitter의 developerWorks 페이지를 살펴보자.
-
AIX 및 UNIX® 포럼에 참여하자.
- AIX Forum
- AIX Forum for developers
- Cluster Systems Management
- Performance Tools Forum
- PowerVM Forum
- 기타 AIX and UNIX Forums
Roger Hill is a multi-certified UNIX systems administrator, integrator, and developer skilled in systems administration as well as building financial and engineering applications employing Perl, C, UNIX shell scripts, CGI scripts, IBM WebSphere, and Microsoft Visual Studio. Roger has maintained, upgraded, and administered multiple UNIX systems, including Sun Solaris, Red Hat Linux, AIX, and HP-UX. You can reach Roger at unixman@charter.net.