2010년 8월 25일 수요일

리눅스의 배포판버전, 커널버전확인방법

출처 : http://www.superuser.co.kr/linux/linux_version/index.htm

리눅스의 배포판버전, 커널버전확인방법

 

 1. 개론

별것 아닌 것 같지만, 리눅스를 처음사용하거나 초보자인경우에는 리눅스의 버전확인하는 방법이 애매할때가 있다.

물론, 자기자신이 설치한 경우에는 기억을 하고 있어서 알겠지만 설치한 기간이 꽤오래된 경우에는 가끔씩 헷갈릴때가 있다.

리눅스설치를 6.0으로 했는지 6.1, 6.2로 했는지 또는 7.0으로 했는지 7.1로 했는지,

그리고 커널버전이 정확히 어떻게 되는지등에 관해서 정확한 버전확인을 원한다면 아래를 참조하기 바란다.

 

 2. 배포판 버전확인

/etc/ 디렉토리에 보면 redhat-release라는 파일이 있다. 이 파일내용에는 배포판의 이름과 버전정보가 있다.

물론, 누군가 수정했다면 이 정보는 신뢰하기 힘들겠지만, 어쨌든 이 정보를 이용하여 배포판의 버전을 확인할수 있다.

 

 3. 커널버전확인

리눅스의 커널버전확인은 간단히 uname -a 로 하면 알수 있다.

 

 

 4. 리눅스 설치 module 정보확인

리눅스박스에 설치된 모듈에 대한 구체적인 파일내역을 확인하려면 아래예와 같이 /lib/modules 디렉토리를 참조하기 바란다.

 

 5. 설치패키지 전체확인

참고로 rpm 명령어를 이용하여 설치된 패키지를 차근차근 살펴보려면 "rpm -qa | more"로 살펴보면 된다.

 

 6. 특정 설치패키지 확인

아래와 같이 "rpm -qa | grep 패키지명"으로 특정 패키지의 설치여부를 확인할수 있다.

 

 7. 초기 설치 패키지 정보확인

/tmp/install.log 파일을 보면 리눅스설치초기시에 설치된 패키지이름들이 들어가 있다.

이 파일도 함께 참조하면 좋을 것 같아서 언급하여본다.


리눅스 - 소유권과 허가권 알아보기

참조 : http://wiki.linuxstudy.pe.kr/wiki.php/linuxstudy8

소유권과 허가권
리눅스 시스템은 앞에서도 말했지만 다중 사용자 시스템이다.
즉 혼자만이 아닌 여러사람이 동시에 같이 사용이 가능한 시스템이라는 이야기다.
따라서 지금 배울 허가권은 유닉스 계열의 시스템에서는 아주 중요한 개념이다.
잘못된 허가권은 시스템에 큰 보안홀을 만들 수 있으므로 잘 사용해야 한다. 

허가권(퍼미션) 이해하기

앞에서 배운 ls 명령을 이용해서 파일 하나를 살펴보도록 하자.
linuxstudy ~ # ls -al /etc/inittab 
-rw-r--r-- 1 root root 1645 Oct 28 13:13 /etc/inittab
linuxstudy ~ #
리눅스의 모든 파일과 디렉토리는 위와 같이 되어 있다.
위의 파일을 풀어서 설명하자면 이렇다.
/etc/inittab 파일은 root사용자와 root그룹의 것이며 root사용자는 읽고 쓸수 있으며
root그룹은 읽기만 되고 root사용자와 root그룹을 제외한 나머지 사용자는 읽기만 되며
파일의 그기는 1645byte 이며 최종적으로 10월28일 13시13분에 수정이 되었다.
뭔가 복잡하게 들릴지 모르겠지만 하나씩 한번 살펴보도록 하자.
리눅스의 기본적인 권한은 세가지로 나눠져 있다.읽기권한(read),쓰기권한(write),실행권한(excute)
-r w xr w xr w x
파일타입소유자소유그룹others

ugo

맨 처음 나오는 - 은 파일타입에 대한 것이다.
참고. 파일 타입
-일반 파일
b블럭형 장치파일(보통 하드 디스크등등)
ccharacter device(보통 장치 파일들,마우스나,콘솔등등)
d디렉토리
p파이프 파일
s소켓파일

컴퓨터는 단순한 기계다.0과1만 이해하는..^^;
이제 rwx 가 숫자로 변경되는 과정을 이해해 보도록 하자. 허가권이 있다면 1 없다면 0이다.
표기r--
비트(2진수)100
8진수2의2승2의1승2의0승
10진수400
읽기 권한만 준다면 숫자는 4로 나온다.

표기rw-
비트(2진수)110
8진수2의2승2의1승2의0승
10진수420
읽고 쓰는 경우에는 4+2가 되서 6

표기rwx
비트(2진수)111
8진수2의2승2의1승2의0승
10진수421
읽고 쓰고 실행하는 권한을 부여하면 4+2+1 해서 7 이 된다.

대충 이해가 되는가? 즉 모든 권한을 다 부여하면 7 이라는 숫자로 되는것이다.
편하게 이해하려면 다음을 보도록 하자.
rwx
421
무조건 4,2,1 이렇게 이해하자.
이제 앞으로 가서 우리가 봤던 /etc/inittab 파일의 퍼미션을 다시 한번 보도록 하자.
r w -(6), r - -(4),r - -(4)
즉 /etc/inittab 파일의 허가권은 644라고 하면 되는것이다.
rwxr-xr-x 는?
r(4),w(2),x(1)/r(4),w(0),x(1)/r(4),w(0),x(1)=7,5,5
자꾸 머리속으로 계산하는 습관을 들이도록 하자..^^;

chmod

이제 위에서 배운 허가권을 변경하는 방법에 대해서 알아보도록 하자.
chmod 명령은 허가권을 변경해 주는 명령으로 시스템 관리자가 되면 아주 많이 사용하게 될 명령어다.
허가권을 변경하는 방법은 두가지의 표현법이 있다.
위에서 배운 숫자로 변경하는 방법과 문자로 부여하는 방법이 있다.
하위 디렉토리까지 같이 변경하기 위해서는 -R 옵션을 사용하면 된다.
chmod 755 /etc/inittab(root에게 읽고 쓰고 실행,root그룹에게 읽고 실행,others에게 읽고 실행)
chmod u+rws,g+rx,o+rx /etc/inittab
위의 두가지는 표현만 다르지 똑같은 결과를 가져온다.
문자로 부여하는 방법은 u(소유자),g(소유그룹),o(others),a(all)과 +,-,= 을 조합해서 허가권을 부여할 수 있다.
+허가권 추가
-허가권 제거
=허가권 유지

특수 퍼미션
이제 설명할것은 기본적인 rwx 권한 이외의 특수한 목적으로 만들어진 퍼미션이다.
SetUID:파일을 실행하는 동안에는 소유자의 권한을 획득하는 허가권.
SetGID:파일을 실행하는 동안에는 소유그룹의 권한을 획득하는 허가권.
Stick Bit:아무나 읽고 쓰기가 가능하지만 생성한 소유자만이 파일을 지울 수 있는 허가권.

SetUID(4),SetGID(2),sticky bit(1) 를 부여하기 위해서는 기본 퍼미션 755 등등의 앞에 붙여주면 된다.
chmod 4755 실행파일(SetUID부여)
chmod 2755 실행파일(SetGID부여)
chmod 1777 디렉토리(Sticky bit부어)
문자 형태로 주고자 한다면 아래와 같다.
chmod u+s 파일
chmod g+s 파일
chod +t 디렉토리

SetUID,SetGID

단순한 설명으로는 이해가 어려울 수 있으니 예를 들어서 설명해 보겠다.
A라는 계정 사용자가 리눅스의 passwd 명령으로 패스워드를 변경한다고 생각해 보자.
당연히 변경된 패스워드는 저장이 될 것이다. 리눅스의 패스워드 파일은 /etc/shadow 라는 파일에 저장이 된다. 그렇지만 /etc/shadow 파일의 퍼미션은 다음과 같이 되어있다.
-rw------- 1 root root 24736 Oct 23 17:54 /etc/shadow
허가권을 알게 되었으니 충분히 이해하겠지만 /etc/shadow 파일은 root는 읽고쓰기,root그룹과others 는 아무 권한이 없는 상태이다.
그런데 어떻게 A라는 계정 사용자가 명령을 내려서 /etc/shadow 파일이 갱신이 될 수 있을까?
바로 여기에 SetUID의 비밀이 숨어있다.
ls -al /bin/passwd 명령을 내려보도록 하자.
-rws--x--x 1 root root 32888 Mar 11  2009 /bin/passwd
실행 권한(x)이 있어야 될 자리에 s 가 보일 것이다.
바로 이게 SetUID 다. 쉽게 설명하자면 시스템의 어떠한 계정이라도 /bin/passwd 파일을 실행하는 동안에는 소유주 즉 root의 권한을 갖는다는 것이다.
물론 실행이 종료되면 다시 A라는 계정의 권한으로 돌아온다..^^;
SetGID 권한은 위와는 동일하지만 소유그룹의 권한을 갖는다는 차이만 있다.
SetUID,SetGID권한은 이렇듯 일반 사용자가 특정사용자나 그룹의 권한을 갖을 수 있으므로
잘못 설정할 경우에는 아주 위험한 경우들이 생겨나므로 조심해야 된다.

나중에 보안 부분을 다루면서 예제를 보여주겠지만 보통 해킹당한 시스템에서 root권한으로 실행되는 백도어등을 많이 심어두고 언제라도 그 파일을 실행해서 root권한을 획득하는 경우가 많다.
그래서 root 비번을 변경하더라도 계속 크래킹이 이뤄지는 것이다.

sticky bit

sticky bit 란 위에서 설명했지만 누구나 읽고 쓸수있는 디렉토리지만 삭제는 생성한 유저만 가능하게 해주는 허가권이다.
보통 sticky bit가 설정되어 있는 대표적이 디렉토리가 있는데 바로 /tmp 디렉토리다.
예를 들어보겠다.리눅스 시스템을 이용해서 여러명의 개발자가 특정 디렉토리에서 프로그램을 개발한다고 가정해 보자.
같은 디렉토리를 공유하기 위해서 디렉토리의 퍼미션을 아래와 같이 설정했다.
chmod 777 /workspace 
즉 모든 사용자가 읽고 쓸수가 있을 것이다.그런데 문제가 생겼다.
A라는 사용자가 같이 작업하는 B의 파일도 역시 읽고 쓸수 있기 때문에 실수로 B사용자의 파일을 지워버렸다. 이런 경우가 발생하지 않기 위해서 sticky bit 를 설정해 주는 것이다.

소유권 이해하기

허가권과 더불어 리눅스 시스템에서 가장 중요한 소유권을 알아보도록 하자.
유닉스 계열의 시스템들은 정확하게 내가 사용할수있는 파일과 사용할 수 없는 파일들이 정확하게 나눠진다. 

chown

파일의 소유자와 소유그룹을 변경하는 명령으로
chown 소유자.소유그룹 파일 및 디렉토리 형태로 실행하면 된다.
하위 디렉토리까지 소유자와 소유그룹을 변경하려면 -R 옵션을 주면 된다.

chgrp

chgrp는 소유그룹만 변경하고자 할 경우에 사용한다. 
chgrp 그룹명 파일 및 디렉토리

앞에서도 설명했지만 시스템 관리자는 많은 사용자의 권한을 적절하게 잘 조정해줘야 한다.
잘못된 권한 설정은 원치않는 결과를 가져올 수 있으므로 주의해서 사용하기 바란다.
특히나 접근이 안된다고 무조건 777로 주는 실수는 범하지 않도록 하기 바란다. 

[CentOS 5.4] 설치 후 설정할것들


CentOS 설치 후 설정

  • 한글 설정
  • /etc/sysconfig/i18n 파일에서 다음을 설정 한다.
LANG="ko_KR.UTF-8"
  • 사용하는 터미널에서 "UTF-8"을 사용하도록 설정 한다.
  • PuTTY의 경우 : Window -> Translation 메뉴에서 "Character set translation on received data"를 "UTF-8"로 설정 한다.
  • 아래 명령을 사용하여 인터넷 접속 여부를 확인 한다.
ping -c3 google.com
  • 서버 시간 설정
echo "00 * * * * root /usr/bin/rdate -s time.bora.net" >> /etc/crontab
  • 기본 업데이트
yum -y update kernel
yum -y update kernel-PAE //--- 32 Bits 시스템에서 메모리가 4GB 이상일 경우
  • 가장 빠른 미러 사이트를 연결하는 yum 플러그인 설치
yum -y install yum-fastestmirror

CentOS Upgrade

  • Upgrade to CentOS 5.4 from 5.3
yum clean all
yum update glibc*
yum update yum* rpm* python*
yum clean all
yum update
shutdown -r now

#--- CentOS 버전 확인
lsb_release -a

CentOS Network 설정

VNC Server 설치

VNC (Virtual Network Computing)는 원격으로 Linux의 GUI 화면으로 접속할 수 있도록 한다.

  • VNC Server 설치
#--- CentOS에 root로 로그인 한다.
yum -y install vnc-server
  • vi /etc/sysconfig/vncservers
VNCSERVERS="1:root"    #--- 1번부터 5901 port를 사용 한다.
VNCSERVERARGS[1]="-geometry 1152x864" #--- 추가 옵션 : -nolisten -nohttpd -localhost
  • VNC Server 사용자 password 생성
cd
mkdir .vnc
cd .vnc
vncpasswd
#--- 여기서 암호를 입력 한다.
service vncserver restart
  • 방화벽에서 해당 사용자의 port를 오픈하여야 한다.
#--- /etc/sysconfig/vncservers에 등록된 1번부터 5901 port를 사용 한다.
  • VNC Client에서 접속 시
VNC Viewer로 접속시 "IP:5901"로 접속 한다.
TightVNC Viewer로 접속시 "IP:1"로 접속 한다.
  • 처음 접속이 완료되면 xstartup 파일이 생성된다. 이를 수정하여 예쁜화면을 사용하자.
cd
cd .vnc
vi xstartup
### twm & #--- 이 라인을 주석 처리(삭제) 한다.
gnome-session & #--- 이 라인을 추가 한다.
service vncserver restart
  • 설치 정보
  • 설치 폴더 :
  • 실행 파일 : /usr/bin/
  • 환경 설정 : /etc/sysconfig/vncservers
  • 기동 종료 : /etc/init.d/vncserver start, stop, restart
  • 서비스 확인 :
  • 참고 문헌

주요 SW 설치

CentOS용 프로그램

  • 시스템 라이브러리 설치
yum -y install zlib curl   
yum -y install gcc g++ cpp gcc-c++ compat-gcc-34-g77
yum -y install libxml2 libxml2-devel
yum -y install gd gd-devel
yum -y install freetype freetype-devel
yum -y install libpng libpng-devel
yum -y install libjpeg libjpeg-devel
yum -y install fontconfig fontconfig-devel
yum -y install mhash mhash-devel
yum -y install libmcrypt libmcrypt-devel
yum -y install openssl openssl-devel
yum -y install gmp gmp-devel
yum -y install flex
yum -y install libtermcap-devel ncurses-devel libc-client-devel bzip2-devel
  • 필요한 경우 컴파일러를 설치 한다.
yum -y install gcc gcc-c++ termcap libtermcap libtermcap-devel gdbm-devel

vsftpd 설치

  • vsftpd 설치
yum -y install vsftpd
  • 아래 설치 정보에 정리된 블랙 리스트에서 ftp를 사용할 사용자를 빼준다.
  • 방화벽에서 20, 21 port를 개방 한다.
  • 설치 정보
  • 실행 파일 : /usr/sbin/
  • 환경 파일 : /etc/vsftpd/vsftpd.conf
  • 블랙 리스트 : /etc/vsftpd/ftpusers, /etc/vsftpd/user_list
  • 기동 종료 : /etc/init.d/vsftpd start, stop, restart, status
  • 서비스 확인 : ps -ef | grep vsftpd | grep -v grep

Java 설치


MySQL 설치

PostgreSQL 설치

Apache Http Server 설치

PHP 설치

Tomcat 설치

  • Linux의 심볼 링크를 사용하여 Tomcat의 Root 폴더를 변경 하는 방법
ln -s /var/www/html /usr/share/tomcat5/webapps/ROOT

Apache ANT 설치

yum -y install ant

부팅시 자동 실행 설정

  • ntsysv 명령어를 실행하고 자동 실행할 데몬을 선택 한다.

사용자 가이드

버전 확인

lsb_release -a
cat /etc/issue

SSH 접속 방법

yum 사용법

  • 관련 폴더
/etc/yum.conf
/etc/yum.repos.d/
/etc/yum/
  • 설치 가능한 package 목록 보기
yum list [all]
yum grouplist #--- package 그룹 보기
  • package 설치 및 삭제
yum install httpd          #--- 설치
yum install httpd.i386 #--- 아키텍처를 지정하여 설치
yum update httpd #--- 업데이트
yum remove httpd #--- 삭제

yum groupinstall "그룹명" #--- package 그룹 설치
yum groupupdate "그룹명" #--- package 그룹 업데이트
yum groupremove "그룹명" #--- package 그룹 삭제
  • 설치된 package 목록 보기
yum list installed [httpd]
  • 업데이트할 package 목록 보기 및 업데이트
yum list updates           #--- 목록 보기
yum update -y #--- 업데이트

RPM 사용법

Redhat Package Manager로 레드헷에서 만든 패키지 관리자 이다.

  • RPM 파일명 구조
패키지이름-버전-릴리즈번호.소스여부.시스템.확장자
  • RPM 명령어
  • rpmfind에서 PRM 패키지(~.rpm)를 찾을 수 있다.
rpm -Uvh gcc-2.96-98.i386.rpm  #--- 패키지 설치
rpm -Uvh ftp://ftp.rpmserver.com/gcc-2.96-98.i386.rpm #--- 네트워크로 패키지 설치
rpm -e gcc-2.96-98.i386 #--- 패키지 제거
rpm -qa | grep httpd #--- 패키지 조회
rpm -qi ~.rpm #--- 패키지 상세 정보 조회
rpm -ql gcc-2.96-98.i386 #--- 패키지 설치 경로 조회
  • RPM 옵션
-i       :기본 설치. 이전 버전이 존재하면 설치하지 않음
-U  : 이전 버전이 설치되어 있으면 업그레이드. (-i 보다 권장)
-F  : 이전 버전이 설치되어 있는 경우에만 설치
-v  : 설치 메시지를 보여 줌
-h  : 진행과정을 '#'으로 표시
-e  : 패키지를 제거
-q  : 패키지가 설치되어 있는 지 확인
-qa  : 현재 설치된 모든 패키지 목록을 출력
-qi  : 현재 설치된 패키지의 간략한 정보를 출력
-ql  : 현재 설치된 패키지의 내용(위치)을 출력
-Vf  : 현재 설치된 파일의 검증. 문제가 없으면 '.'으로 표시
-Va  : 한 패키지만 검증
--force  : 충돌 등을 무시하고 무조건 설치
--nodeps : 의존성 문제를 무시하고 설치

관리자 가이드

방화벽 설정

CentOS는 디폴트로 iptables를 방화벽으로 사용 한다.

  • 특정 IP의 서버 접근 차단 방법
iptables -A INPUT -s 61.247.209.81 -j DROP
iptables -A INPUT -s 61.247.209.0/24 -j DROP

KVM

  • 참고 문헌

VirtualBox 설치

RPM 제작

  • RPM 제작을 위한 패키지 설치
yum -y install rpm-build rpm-devel
yum -y install ~.rpm
  • 설정 파일 : /etc/rpmrc, /usr/lib/rpm/rpmc


  • RPM 소스 패키지(~.src.rpm) 다운로드
  • rpmfind에서 원하는 RPM 소스 패키지를 다운로드 한다.
wget ftp://rpmfind.net/linux/fedora/development/source/SRPMS/~.src.rpm


  • RPM 소스 패키지로 RPM 패키지(~.rpm) 만들기
rpmbuild --rebuild --target=i686 ~.src.rpm
rpmbuild --rebuild ~.src.rpm #--- target은 생략 가능


  • RPM 패키지 설치
rpm -Uvh ~.rpm


  • RPM 소스 패키지 내용 보기
mkdir /usr/src/redhat
rpm -ivh ~.src.rpm
  • /usr/src/redhat/SOURCES : 소스와 패치 파일 저장
  • /usr/src/redhat/SPECS  : 스팩 파일 저장


  • RPM 소스 패키지 다시 만들기
cd /usr/src/redhat/SPECS
rpm -ba ~.spec
  • /usr/src/redhat/SRPMS : RPM 소스 패키지가 생성됨
  • /usr/src/redhat/RPMS  : RPM 패키지가 생성됨


  • /usr/src/redhat/ 폴더의 구조
  • BUILD  : rpm에 의해서 빌드가 이루어지는 디렉토리
  • RPMS  : 아키텍처별 RPM 패키지 저장
  • 아키텍처별로 athlon, geode, i386, i486, i586, i686, noarch 하위 폴더가 있음
  • SOURCES : RPM 소스 저장
  • SPECS  : RPM Spec 저장
  • SRPMS  : SRPM 저장

[CentOS 5.4] PHP에서 파일업로드 않될때


기본적으로 PHP에서 파일 업로드가 않되는 이유는 두가지정도로 알고있다.

첫번째, httpd 서비스 실행자인 nobody에게 read/write 권한을 주었느냐이다.
두번째, php.ini 환경설정에서 file_uploads 옵션이 off로 되어있는지 확인하는것이다.

첫번째 문제라면은 웹 루트폴더 (DocumentRoot)에 가서 권한설정 명령어를 때려준다.
# chown -R nobody htdocs/

두번째 문제일때
# cd /usr/local/apache/conf/
# vi php.ini
여기서 [ : ]을 누르면 화면하단에 [ : ] 표시가 보일것이다~
이것이 vi 편집기 명령어를 사용할수 있게해준다.

[ :/file_uploads ] 라고치고 엔터!! ( 동일 패턴을 검색해주는것이다~ 찾기기능 )
file_uploads = ON 으로 되어있는지 확인하고 저장 [ :wq ] 하고 나온다.

마지막으로 httpd (아파치) 서비스를 다시시작해준다.

# /etc/init.d/httpd restart

2010년 8월 24일 화요일

CentOS 5.4(5.5 Updatede) APM 설치 방법 - 확인!!

TestServer - CentOS 5.5 APM 설치 스크립트 - 작성일 : 2010년 08월 24일

TestServer 사양
OS : CentOS 5.4 / CPU : P4 2.8Ghz / RAM : 1G / HDD : 250GB

Apache, MySQL, PHP 설치이후 Apache가 실행되지 않을때,
혹은 php5 modules 이 문제가 발생되었다고 할때는 제일 아래 하단을 봐주센~ ^^

### APM 다운로드
- 섭군이는 필요한 소스를 다음 디렉토리에 저장하기로 했음~ 각자에 맞게 바꾸면 됩니다~
# /usr/local/src/
- apache 다운로드 : http://www.apache.org/
# wget http://mirror.tw/pub/apache/httpd/httpd-2.2.14.tar.gz
- php 다운로드 : http://www.php.net/
# wget http://kr.php.net/get/php-5.3.1.tar.gz/from/this/mirror
- mysql 다운로드 : http://www.mysql.org/
# wget http://dev.mysql.com/get/Downloads/MySQL-5.1/mysql-5.1.41.tar.gz/from/http://mirror.khlug.org/mysql/

 
1. MySQL 설치

 1) 다운로드한 파일 압축 해제
# tar -zvxf mysql-5.1.41.tar.gz
# cd mysql-5.1.41
 
 2) mysql 계정 생성
# useradd -M -s /bin/false mysql
 
 3) ./configure 실행
# ./configure \
--prefix=/usr/local/mysql \
--with-charset=utf8 \
--with-extra-charsets=all
 
 4) make && make install(설치경로 -> /usr/local/mysql)
# make && make install
 
 5) 환경설정파일 복사(support-files/ -> /etc/my.cnf)
# cp /usr/local/apm/mysql-5.1.41/support-files/my-large.cnf /etc/my.cnf
 
 6) 기본 DB 생성
# cd /usr/local/mysql
# bin/mysql_install_db --user=mysql
 
 7) 디렉토리 권한 설정
# chown -R root .
# chown -R mysql var
# chgrp -R mysql .
 
 8) 환경변수 등록(경로 추가)
# vi /root/.bash_profile
# PATH=$PATH:$HOME/bin:/usr/local/mysql/bin <- 수정
# source ~/.bash_profile
 
 9) 자동실행 설정
# cp share/mysql/mysql.server /etc/init.d/mysqld
# chkconfig --add mysqld
# chkconfig --list|grep mysqld
 
 10) root 암호 등록 및 테스트
   - mysql을 처음 설치하고나면 생성되는 root 계정에는 기본적으로 암호가 없다. 그러므로 암호설정을 반드시 해준다.
# /etc/init.d/mysql start
# Start MySQL          [ OK ]
# mysqladmin -u root password 1q2w3e4r
# mysql -u root -p
 
 
==========================================================================================================================
 
2. Apache 설치

 1) 소스를 받은 위치로 이동한다.
        기본경로1 : /usr/local/apm
# cd /usr/local/apm
 
 2) 다운로드한 파일 압축을 풀어준다.
# tar -zxf httpd-2.2.14.tar.gz
# cd httpd-2.2.14

 3) 압축이 풀린 디렉토리로 이동한다.
       기본경로2 : /usr/local/apm/httpd-2.2.14
# cd httpd-2.2.14
 
 4) 컴파일 환경설정을 한다. 5분가량 소요된다.
# ./configure \
--prefix=/usr/local/apache \
--enable-mods-shared=all \
--enable-so \
--enable-rewrite
 
 5) 설치를 진행한다. make 와 make install 두 과정을 합해서 15분~20분 가량 시간이 소요된다.
       기본경로3 : /usr/local/apache/
# make && make install
 
 6) 서버를 재부팅할경우 apache 데몬이 자동실행이 되도록 설정한다.
# cp bin/apachectl /etc/init.d/httpd
# vi /etc/init.d/httpd  <- vi로 httpd파일을 열고 아래내용을 맨 밑에 추가한다.
# chkconfig: 2345 90 90
# description: init file for Apache server daemon
# processname: /usr/local/server/apache/bin/apachectl
# config: /usr/local/server/apache/conf/httpd.conf
# pidfile: /usr/local/server/apache/logs/httpd.pid

# chkconfig --add httpd        ntsysv에 httpd 항목 추가
# chkconfig --list|grep httpd       ntsysv에 httpd 항목이 제대로 추가됐는지 확인
 
 7) 환경설정은 conf/httpd.conf 를 수정하면 된다.
# vi conf/httpd.conf
 
<IfModule !mpm_netware_module>
<IfModule !mpm_winnt_module>
#
# If you wish httpd to run as a different user or group, you must run
# httpd as root initially and it will switch.
#
# User/Group: The name (or#number) of the user/group to run httpd as.
# It is usually good practice to create a dedicated user and group for
# running httpd, as with most system services.
#
User nobody <- daemon=>nobody
Group nobody <- daemon=>nobody

</IfModule>
</IfModule>

 8) 테스트를 위해 아파치 데몬 실행 및 웹브라우저로 접속을 해본다.
 # /etc/init.d/httpd start      아파치 데몬 시작

 
==========================================================================================================================
 
 
3. PHP 설치

 1) 소스를 다운로드한 위치를 각자 확인한다.
     기본경로1 : /usr/local/apm/
 
 2) 다운로드한 파일의 압축을 해제 한 후 압축풀린 디렉토리로 이동한다.

# tar -zxf php-5.3.1.tar.gz
# cd php-5.3.1
 
 3) 기본경로2 : /usr/local/apm/php-5.3.1
      컴파일하기 위한 환경변수 설정을 해준다.
 
# ./configure \
--prefix=/usr/local/php \
--with-apxs2=/usr/local/apache/bin/apxs \
--with-mysql=/usr/local/mysql \
--with-config-file-path=/usr/local/apache/conf \
--disable-debug \
--enable-safe-mode \
--enable-track-vars \
--enable-sockets \
--with-mod_charset \
--with-charset=utf8 \
--with-xml \
--with-language=korean \
--enable-mailparse \
--enable-calender \
--enable-sysvsem=yes \
--enable-sysvshm=yes \
--enable-ftp \
--enable-magic-quotes \
--enable-gd-native-ttf \
--enable-url-include \
--enable-trans-id \
--enable-inline-optimization \
--enable-bcmath \
--with-jpeg \
--with-png \
--with-zlib \
--with-jpeg-dir=/usr \
--with-png-dir=/usr/lib \
--with-freetype-dir=/usr \
--with-libxml-dir=/usr \
--enable-exif \
--with-gd \
--with-ttf \
--with-gettext \
--enable-sigchild \
--enable-mbstring

 4) 컴파일 및 인스톨을 진행한다. 20분 가량 걸린다. 담배나 한대 피자..

# make && make install

 5) 환경설정
# cp php.ini-production /usr/local/apache/conf/php.ini    php 설정파일을 아파치가 설치된 디렉토리의 conf 디렉토리로 복사
# vi /usr/local/apache/conf/httpd.conf    아파치 환경설정파일을 열어 아래의 내용을 추가/수정 한다.
 
# 수정
<IfModule dir_module>
 DirectoryIndex index.html index.htm index.php
</IfModule>
 
# AddType 지시어 추가
<IfModule mime_module>

  AddType application/x-compress .Z
  AddType application/x-gzip .gz .tgz

  밑에부분에 추가

  AddType application/x-httpd-php .php .htm .html .inc .php4 .php3
  AddType application/x-httpd-php-cource .phps

</IfModule>

 6) php 설치가 제대로 됐는지 테스트 해본다.

# /etc/init.d/httpd restart    바뀐 환경설정 적용을 위해 아파치데몬 재시작

[참고] 아파치 가동시 아래와 같은 에러가 발생할 경우
Syntax error on line 232 of /usr/local/apache/conf/httpd.conf:
Cannot load /usr/local/apache/modules/libphp5.so into server: cal/apache/modules/libphp5.so: cannot restore segment prot after reloc: Permission

###############################################
오류에 대한 해결 방법
# vi /etc/sysconfig/selinux
SELINUX=disable   <<== disable로 변경하고 재부팅한다
###############################################

# vi /usr/local/server/apache/htdocs/phpinfo.php
phpinfo.php 파일 생성 및 내용 입력후 저장
<? phpinfo(); ?>
 => http://localhost/phpinfo.php 각자의 환경에 맞는 주소입력 요망.
    phpinfo.php가 웹브라우저에서 제대로 표시되면 정상적으로 설치된것이다.
 

아이폰 테마 ms6620님 요청



ms6620님 요청 테마

Wallpaper.html , Wallpaper 삭제 ==> Wallpaper.png 파일로 교체

Private/Background.png ==> Private/Background.jpg 파일로 교체

LockBackground.html 파일 소스 일부 수정 ==> 배경화면부분

Bundles/com.apple.chatkit/Balloon_1.png, Balloon_2.png ==> 파일교체

락스크린 시간 부분 ==> 새벽시간 PM, 밤11시이후 AM 이라고 뜨는것을 수정하였습니다.

크게 변형할것도 없었는데 일때문에 일찍이 못해드렸네요~ 죄송합니다 ㅠㅠ

압축파일은 WinRAR 최신버젼을 사용하였습니다.

2010년 8월 20일 금요일

[클래스] 자리수 지정한 문자로 채우기 str_pad()

출처 : http://www.saybox.co.kr/bbs/board.php?bo_table=board02&wr_id=137


PHP에서 특정 자리수 만큼 지정된 문자로 변경하고자 할 때 간단하게 str_pad() 함수를 이용하면 편리 합니다.
예를들어 1~20까지의 숫자가 있는데 1~9까지는 2자리수로 맞혀 주기 위해 앞에 "0"을 붙이고자 할 경우

str_pad($a, 2, "0", STR_PAD_LEFT);

라고 하면 간단하게 1~9 인경우 앞에 "0"이 붙게 됩니다.
기타 옵션에 따른 결과를 간단히 설명하겠습니다.

<?
$input = "test";
echo "<br>1:".str_pad($input, 8)."s";
echo "<br>2:".str_pad($input, 8, "*", STR_PAD_LEFT)."s";
echo "<br>3:".str_pad($input, 8, "*", STR_PAD_BOTH)."s";
echo "<br>4:".str_pad($input, 5 , "***")."s";
?>

결과 :
1:test    s
2:****tests
3:**test**s
4:test*s

1번은 변경할 문자를 입력하지 않을 경우 " " 부족한 자릿수 만큼 우측에 공백으로 추가합니다.
2번의 경우는 LEFT 부족한 자리수 만큼 왼쪽부터 채우며,
3번 BOTH는 좌,우 모두 균등하게 배치합니다.
만약 남은 공백이 5칸인경우 좌측에 2칸, 우측에 3칸을 추가 합니다.
4번은 변경할 값이 *** 3자리 이지만 자리수를 5칸으로 지정 했기에 * 하나만 적용되었습니다.

다양한 옵션으로 상황에 맞게 적용 하시면 됩니다.

2010년 8월 19일 목요일

짜집기 테마 - 신민아

최근 수정일 : 2010년 8월 25일 10시 10분

오랜만에 들어와서는 느닷없이 아이폰 테마부터 올려주는 쎈쓔 ㅋㅋ;;

안녕하세요~ 섭군입니다 ^^
너무 오랜만이죠??
제가 그동안 너무 소홀했었네요... 근데 또 소홀해질꺼 같아요 ㅎㅎㅎㅎ
아차차!!

이번에 포스팅할글은 아이폰 3GS iSO 4.0.1 테마입니다.
네이버 카페 [ 아사모 ] 에서 많은분들이 올려주신 테마를 기초로 하였구요~
간단하게 소개만해 드리도록하겠습니다.

테마명 : Theme_sinminah

준비물
1 - 해킹된 아이폰
2 - 윈터보드
3 - 하이드클럭락스크린 (맞나?ㅋㅋ)
4 - iBlank
기타 개인적인 어플들 ㅋㅋ

말그대로 완전 짜집기 집대성한 테마입니다.
어떤분의 요청도 있었고 저도 테마를 쓰다보니 테마 만들고 하는것에 빠지게 되었어요 ㅋㅋ

테마 적용법
1. //private/var/stash/Themes.******/ 폴더에 테마 폴더를 넣어준다.
    폴더에는 반드시 [ .theme ] 라는 확장명이 붙어줘야한다
2. 윈터보드로 가서 Theme_sinminah.theme 를 선택한다
3. 리스프링(적용)해준다

- 압축파일은 7zip을 이용하였습니다. (압축률이 뛰어나서요 ㅋㅋ)
-  기존에 락스크린을 보시면 전체적으로 글씨들이 커서 화면 밖으로 튀어나가있었는데 그놈들을 잡아주었습니다.

- 아이콘들을 이쁜것들로 구해보려고 했지만 만들지는 못하고 여기저기서 줏었어요 ㅡㅡ;;
- 위젯 아래사진을 보시면 로딩중이라고 나오는데... 사실 폰이 정지가되서리 ㅋㅋㅋㅋㅋㅋ (아놔 ㅠ)

============================================================================================

수정2
- 락스크린의 날짜에 25일 파일이 없어서 않떠버리네요;; 그놈 만들었습니다.
- 락스크린의 수요일이 튀어나가는거 수정하였습니다.
- 홈스크린 볼륨조절할때 없어지는놈 기본 디폴트로 변경하였습니다.

이상 간단한 소개를 마치고 적용하는부분에 있어서 궁금하신분은 리플을 달아주시거나
네이트 xdream3817@nate.com 로 쪽찌 날려주세요 ^^



=== 테마 다운로드 ===
http://www.7-zip.org/ << 요기 가셔서 7zip 다운 받고 압축푸세요~
다른걸로 압축풀면 당근 에러가 뜹니다.

64 bit CentOS 5.x 에서 Flash Player 설치하기

CentOS 5.3 64bit를 설치한 후 FireFox를 실행하고 네이버에 들어오면 Flash Player를 설치하라는 메시지가 친절하게도 나온다.

하지만 Adobe 사이트로 들어가 다운로드 받아 설치해 봐도 Flash가 있는 화면은 회색 네모 상자로만 보인다.  

그 이유는 adobe flash player가 아직 64bit 리눅스를 지원하고 있지 않기 때문에 아무리 가장 최신 버전으로 설치해 봐도 64bit Firefox에서는 flash를 볼 수 없다고 한다.

해결방법은 32bit Firefox를 설치하면 된다 (-_-)a

64bit FireFox에서 Flash Player설치하기

사실 32bit FireFox를 설치한다는 것은 틀린 말은 아니지만 64bit Firefox에서도 편법이지만 Flash를 볼 수 있는 방법은 있다.
그것은 바로 nspluginwrapper라는 패키지를 사용하면 된다. 이제 nspluginwrapper를 설치하여 Flash를 보는 방법을 진행해보자.
Nspluginwrapper
는 이름 그대로 크로스-플랫폼 넷스케이프4(NPAPI) 호환 플러그인 뷰어로 64bit 어플리케이션에서 32bit 플러그인을 사용할 수 있도록 해주는 wrapper .

 

먼저 nspluginwrapper 를 설치하자.

# yum install curl compat-libstdc++-33 glibc nspluginwrapper

[root@localhost ~]# yum install curl compat-libstdc++-33 glibc nspluginwrapper
Loaded plugins: fastestmirror

Loading mirror speeds from cached hostfile

 * base: ftp.daum.net

……<어쩌구저쩌구 쭈욱 계속 진행.. 그러다가 !!!!>

Is this ok [y/N]: y  // ß 의존성 여부를 확인후 nspluginwrapper 설치여부를 물어본다. Y를 선택

 

다음은 Flash Player를 설치합니다.

n  Adobe 사이트에서 Flash Player를 다운받습니다 http://get.adobe.com/kr/flashplayer/

n  다운받은 파일을 설치합니다.

# rpm -ivh /tmp/flash-plugin-10.0.32.18-release.i386.rpm

마지막으로 열려있는 FireFox를 모두 닫고 새로이 FireFox를 열어봅니다.

     

이제 Flash가 정상적으로 잘 보이는 것을 확인할 수 있다.

Crontab(크론탭) 사용 방법

크론탭이란놈을 처음접하며.... 이거 머하는거여??
역쉬 개발자 왕!!초보이다보니 아~~ 이런것도 있구나~ 하고 있었다 ;;;
혹시 다음에 또 쓸일 있을지 모르니 기억해두자!!^^


1. cron
   - 일정시간 마다 시스템에서 자동으로 실행 시키는 데몬(Windows의 작업스케줄러와 비슷한 기능)

   - cron을 사용할때 crontab이라는 명령을 이용해서 실행(/etc/crontab)

   - 각 사용자가 등록한 crontab은 [리눅스 : /var/spool/cron/사용자명,  솔라리스 : /var/spool/corn/crontabs/사용자명] 저장됨

   - 현재 cron deamon이 돌고 있는지 확인
           ps -ef | grep cron
   - cron deamon kill
            kill -9 "pid of cron"
   - deamon 재실행
            /usr/sbin/cron


2. cron 데몬의 실행과 종료
   실행 : /etc/rc.d/init.d/crond [start/restart/stop]

 

3. crontab  
   - 지정된 시간에 다른 프로그램을 실행하면서 연속적으로 실행하는 프로그램

   - 유사한 명령으로는 at 명령어가 있음

 

4. crontab와  at 명령어의 차이점

   - crontab : 일정한 간격으로 계속해서 명령을 실행함

   - at : 지정된 명령을 한번밖에 수행하지 않음

 

5. crontab 옵션 
  #crontab [파일][-u사용자]     crontab을 사용자파일로 대체
  #crontab  -[-u사용자]            crontab을 표준입력으로 대채
  #crontab -l[사용자]               사용자를 위한 리스트를 보여줌
  #crontab -e[사용자]              사용자를 위한 crontab을 에디트 함
  #crontab -d[사용자]              사용자를 위한 crontab을 제거

 

6. crontab 작업형식

      [MM] [HH] [DD] [mm] [d] [command]

필드

 의미

 범위

 첫 번째  분  0~59
 두 번째  시  0~23
 세 번째  일  1~31
 네 번째  월  1~12
 다섯 번째  요일  0~7 (0,7 : 일요일, 1 : 월요일)
 여섯 번째  명령어  실행할 명령을 한줄로 쓴다.

    -  구분자는 space(공백)으로 구분

    -  시간을 나타내는 각 필드에서는 와일드 카드'*'를 사용할 수 있음

    -  각각의 요일을 구분할 때는 ','를, 연일을 나타날때는 '-'를 사용 즉, '1,3' : 월요일과 수요일, '1-5' : 월요일부터 금요일까지  
    -  한 줄당 하나의 명령 (두줄로 나눠서 표시할 수 없음)
    - # 으로 시작하는 줄은 실행하지 않는다.

 

7. crontab 사용 방법

          7-1) crontab 조회(-l 옵션)

                 [root@linux root]#crontab -l
                  → 작업목록을 보여준다.

                  → no crontab for truefeel(설정한 적이 없어 아직 비어있음)

 

          7-2) crontab

                 [root@linux root]#crontab aaa
                 → aaa 란 파일을 crontab 으로 사용(aaa 에는 이미 crontab 형식에 맞에 입력되어 있어야함)

 

          7-3) crontab 설정(-e 옵션) : 환경변수 EDITOR에 따라 다른 에디터를 사용할 수 있음

              ※ 시간 설정 몇가지 의미
                 - '*'표시는 해당 필드의 모든 시간을 의미
                 - 3,5,7와 같이 콤마(,)로 구분하여 여러 시간대를 지정할 수 있음
                 - 2-10와 같이 하이픈(-)으로 시간 범위도 지정할 수 있음
                 - 2-10/3와 같이 하이픈(-)으로 시간 범위를 슬래쉬(/)로 시간 간격을 지정할 수 있다

                    (2~10시까지 3시간 간격으로. 즉, 3, 6, 9시를 의미함)

 원하는 시간

 형식

 매주 토요일 새벽 2:20                      20  2 *  *  6  명령어
 매일 오후 4,5,6시    0  4-6   *  *  *  명령어
 매일 2시간 간격으로 5분대에  5  */2 *  *  * 명령어
  매월 1일 새벽 1:15  15  1   1  *  * 명령어
  1,7월 1일 새벽 0:30  30  0   1  1,7  * 명령어

                                              

                  1. 매시 1회 자동실행하기 위한 시스템 크론 설정
                      01 * * * * root run-parts /etc/cron.hourly
                   - 매일 매시 01분마다 /etc/cron.hourly 디렉토리내에 존재하는 파일들을 실행
 
                  2. 매일 1회 자동실행하기 위한 시스템 크론설정
                      02 4 * * * root run-parts /etc/cron.daily
                   - 매일 새벽 4시 02분마다 /etc/cron.daily  디렉토리내에 존재하는 파일들을 실행
 
                  3. 매주 1회 자동실행하기 위한 시스템 크론설정
                      22 4 * * 0 root run-parts /etc/cron.weekly
                   - 매주 일요일 새벽 4시 22분마다 /etc/cron.weekly 디렉토리내에 존재하는 파일들을 실행
 
                  4. 매월 1회 자동실행하기 위한 시스템 크론설정
                     42 4 1 * * root run-parts /etc/cron.monthly
                  - 매월 1일 새벽 4시 42분마다 /etc/cron.monthly 디렉토리내에 존재하는 파일들을 실행

     

                  5. 설정예 : 한국표준시간 연구소에서 매일 새벽 1시에 표준시간을 가지고 오도록 설정할 경우
                     00 1 * * * root rdate -s time.kriss.re.kr && clock -w

             

                  6. 월,수,금 오전 4시에 notice 라는 문서의 내용을 메일로 발송한다.
                      0 4 * * 1,3,5 cat /root/notice | mail -s "notice" mailID@naver.com

          7-4) root 이외의 사용자에게 crontab 명령어를 이용할 수 있게 하는 방법
                 - /etc/cron.allow 파일에 사용자의 id를 등록
                 - 일반사용자의 crontab 명령어 사용을 제안하고자 한다면 : /etc/cron.deny 파일에 사용자의 id 를 등록

 
         7-5) FAQ
               1) cron 설정한 후에는 crond 데몬을 재실행해야 하나요?
                   - 아닙니다. crontab -e 으로 설정 후 빠져나오면 바로 적용됩니다.
              2) truefeel 사용자는 cron을 못 쓰게 하고 싶습니다.
                 /etc/cron.allow : 허용할 사용자 ID 목록
                 /etc/cron.deny  : 거부할 사용자 ID 목록
                cron.allow 파일이 있으면 이 파일에 들어있는 ID만 사용 가능
                cron.deny  파일이 있으면 이 파일에 들어있는 ID는 사용 불가
                따라서 cron.deny에 truefeel ID를 추가해주면 됩니다.
               3) > /dev/null  2>&1 이 무슨 뜻입니까?
                  - 지정한 명령어 처리 결과와 발생할지 모르는 에러메시지를 출력하지 않고 모두 버린다는(/dev/null)는 뜻입니다.
                    만약 결과와 에러를 파일로 저장하려면 /dev/null 대신 파일명을 적어주면 됩니다.

 
 
 
 
  /etc/crontab내용과 설명
      SHELL=/bin/bash
      PATH=/sbin:/bin:/usr/sbin:/usr/bin
      MAILTO=root
      HOME=/

      # run-parts
      01 * * * * root run-parts /etc/cron.hourly
      #시간 단위로 실행시키 프로그램입니다.
      # /etc/cron.hourly디렉토리에 있는 내용을 모두 실행합니다.
      02 4 * * * root run-parts /etc/cron.daily
      # 일단위입니다.
      22 4 * * 0 root run-parts /etc/cron.weekly
      # 주단위 입니다.
      42 4 1 * * root run-parts /etc/cron.monthly
      # 월단위 입니다.

Unix(Linux) Shall Script 기초 강좌좌

원문: http://www.linuxnewbie.org/nhf/intel/programming/introbashscript.html

쉘 스크립트?? 이건 머하는거얌 ㅋㅋ
잊지 않기위해.... 하아~~ 이런거 너무 어려워 ㅠㅠ

리눅스에서 쓸 수 있는 모든 쉘들처럼, BASH(Bourne Again Shell)은 뛰어난 명령 라인 쉘이면서, 그 자체로도 하나의 스크립팅 언어이다. 당신은 쉘 스크립팅을 이용해서 쉘이 가진 능력을 충분히 활용할 수 있으며, 쉘 스크립팅이 아니었으면  수많은 명령을 필요로 했을 많은 일들을 자동적으로 처리할 수도 있다. 당신의 리눅스 박스에 놓여 있는 많은 프로그램들은 쉘 스크립트들이다. 만일 쉘 스크립트가 어떻게 작동하는지 배우고 싶거나 당신이 가지고 있는 쉘 스크립트를 수정하고 싶다면, bash 문법을 이해하는 것은 필수적이다. 게다가, bash 언어를 이해하면 정확히 당신이 원하는 방식으로 일을 하는 당신 자신의 프로그램을 작성할 수 있다.
 

프로그래밍 또는 스크립팅?
프로그래밍을 처음 하는 사람들은 대개 프로그래밍 언어와 스크립팅 언어 사이의 차이를 혼동한다. 프로그래밍 언어는 일반적으로 스크립팅 언어에 비해 보다 강력하고 보다 빠르다. 프로그래밍 언어의 예로는 C, C++, Java가 있다. 프로그래밍 언어는 대개 소스 코드(최종 프로그램이 어떻게 실행될 것인가에 대한 지시문을 담고 있는 텍스트 파일)에서 시작해서 컴파일 과정을 통해 실행 가능 파일로 만들어 진다(built). 이렇게 해서 만들어진 실행 가능 파일은 다른 운영 체제로 쉽게 이식되어지지 않는다. 예를 들어, 당신이 리눅스에서 C 프로그램을 작성했다면, Windows 98시스템에서는 그 프로그램을 실행할 수 없을 것이다. 프로그램을 실행하기 위해서는, Windows 98 시스템 하에서 소스 코드를 다시 컴파일해야만 한다. 스크립팅 언어 역시 소스 코드에서 시작을 하지만, 실행 가능 파일로 만들기 위한 컴파일 과정이 없다. 대신, 번역기(interpreter)가 소스 파일에서 지시문을 읽고 각 지시문을 실행시킨다. 불행히도, 번역기가 각 지시문을 하나 하나 읽어야만 하기 때문에, 일반적으로 번역기를 통해 실행되는 프로그램은 컴파일된 프로그램보다 느리다. 스크립팅 언어의 가장 큰 장점은 소스 파일을 어떤 운영 체제에나 쉽게 이식할 수 있으며 바로 그 자리에서 번역기를 통해 실행할 수 있다는 것이다. 이런 점은 작은 프로그램에서는 장점으로 여겨질 수 있지만, 큰 규모의 어플리케이션을 작성할 것을 계획하고 있다면 프로그래밍 언어를 사용하는 편이 알맞다. 스크립팅 언어의 예로는 Perl, Lisp, Tcl이 있다.

무엇을 알아야 하는가?
당신 자신의 쉘 스크립트를 작성하기 위해서는 매일 사용하는 기본적인 리눅스 명령어를 알아야 한다. 예를 들면, 어떻게 파일을 복사, 이동하고 새로운 파일을 만드는지 하는 것들을 알아야만 한다. 반드시 알아두어야 할 또 한 가지는 텍스트 편집기를 쓰는 방법이다. 리눅스의 대표적인 세 가지 텍스트 편집기는 vi, emacs, pico이다. 만일 vi나 emacs를 사용하는데 익숙하지 않다면, pico나 사용하기 쉬운 다른 텍스트 편집기를 이용하라.

주의!!!
루트 사용자인 상태에서는 절대로 스크립팅을 연습하지 않도록 하라! 어떤 일이 일어날 지 모른다. 만일 당신이 코딩을 하는 도중 우연히 실수를 해서 시스템을 망쳤다 해도 나는 책임질 수 없다. 루트 권한이 없는 일반 사용자 계정을 사용하라. 당신은 스크립팅 연습을 하기 위한 새로운 사용자를 만들기를 원할 지도 모른다. 이 경우, 일어날 수 있는 가장 나쁜 일은 새로 만든 사용자 디렉토리가 없어지는 것에 불과하다.

첫번째 BASH 프로그램
우리의 첫번째 프로그램은 고전적인 "Hello World" 프로그램이 될 것이다. 만일 당신이 이전에 프로그램을 해본 경험이 있다면, 지금까지 수많은 "Hello World" 프로그램을 봐왔을 것이다. 하지만, 이것은 전통이고, 누가 그 전통을 바꾸겠는가? 이 "Hello World" 프로그램은 단순히 "Hello World"란 문구를 화면에 프린트하는 것이다. 그럼 우선 텍스트 편집기를 시작해서, 그 안에 다음의 내용을 입력하라:

#!/bin/bash
echo "Hello World"

첫번째 라인은 리눅스에게 이 스크립트를 실행하기 위해서 bash 번역기를 사용하라고 알려준다. 이 경우에는, bash는 /bin 디렉토리에 있다. 만일 bash가 시스템의 다른 디렉토리에 있다면, 그 위치를 첫번째 라인에 써주어라. 스크립트 안에 있는 지시문을 실행할 때 어떤 인터프리터를 사용할 것인지 리눅스에게 알려주는 것이므로 번역기를 정확하게 명시하는 것은 매우 중요하다. 다음엔  스크립트를 hello.sh란 이름으로 저장하자. 모두 다 되었으면, 스크립트를 실행 가능하도록 한다:

xconsole$ chmod 700 ./hello.sh

파일의 퍼미션을 어떻게 바꾸는지 모른다면 chmod에 대한 매뉴얼 페이지를 참조하라. 일단 위의 명령을 실행하면 단지 파일 이름을 타이핑하는 것만으로 당신의 프로그램을 실행할 수 있다:

xconsole$ ./hello.sh
Hello World

됐다! 당신의 첫번째 프로그램이다! 그 자체로는 지루하고 쓸모없지만, 모든 사람이 이런 식으로 프로그래밍을 시작한다. 첫번째 프로그램을 실행하기까지의 과정을 되새겨 보자. 코드를 작성하고, 파일을 저장하고, chmod 명령으로 실행 가능하도록 만들었다.

명령들, 명령들, 명령들
당신의 첫번째 프로그램이 한 일은 정확히 무엇인가? 그 프로그램은 화면에 "Hello World"란 문구를 프린트했다. 하지만 어떻게 그렇게 한 것일까? 프로그램은 명령을 사용했다. 프로그램 상에서 당신이 썼던 딱 한 줄의 코드는 echo "Hello World"였다. 그렇다면, 어떤 것이 명령인가? 바로 echo이다. echo 프로그램은 하나의 인자를 취해서 그 인자를 화면에 프린트한다.

인자(argument)는 프로그램 이름을 입력한 다음에 따라온다. 첫번째 예제에서 당신이echo에 넘겨 준 인자는 "Hello World"이다. 당신이 ls /home/root란 명령을 입력할 때, ls에 대한 인자는 /home/root이다. 그렇다면 이 모든 것이 뜻하는 것은 무엇인가? 만일 당신이 인자 하나를 취해서 화면에 프린트하는 프로그램을 가지고 있다면, 그 프로그램 대신에 echo를 사용할 수 있다는 것이다. 우리가 foo라고 하는 프로그램을 가지고 있다고 가정하자. 이 프로그램은 문자열 하나를 인자로 가져서  화면에 출력한다. 우리는 위의 예제를 다음과 같이 다시 쓸 수 있다:

#!/bin/bash
foo "Hello World"

이 내용을 저장하고 chmod 로 실행 가능하도록 하여 실행하자:

xconsole$ ./hello
Hello World

결과는 정확하게 똑같다. 프로그램 이름을 제외하고 어느 한 곳이라도 다른 곳이 있는가? 전혀 없다. 실제로 무엇인가 작성한 것이 있는가? 당신이 echo 프로그램을 작성했다면 모르지만 그렇지 않다면 전혀 작성한 것이 없다. 당신이 한 것이라고는 이미 주어진 인자와 함께 echo 프로그램을 당신의 쉘 프로그램에 사용한 것 뿐이다. 실제로 echo 명령 대신 쓸 수 있는 명령은 printf 가 있다. 특히 C 프로그래밍에 능한 사람의 경우에는printf의 여러 기능을 이용해서 보다 다양한 결과를 낼 수 있다. 사실, 쉘 프로그램을 만들지 않고도 위의 예제와 꼭 같은 결과를 얻을 수 있다:

xconsole$ echo "Hello World"
Hello World

bash 쉘 스크립팅은 다양한 제어를 가능하게 하며 또한 배우기 쉽다. 방금 보았듯이, 쉘 프로그램에 리눅스 명령을 함께 쓸 수 있다. 쉘 프로그램은 특정한 일을 하기 위해 특별히 조립된 여러 프로그램의 집합체이다.

보다 유용한 프로그램
이제 우리는 모든 파일을 하나의 디렉토리로 옮기고, 그 디렉토리를 내용물 모두와 함께 지운 뒤, 다시 그 디렉토리를 만드는 프로그램을 만들 것이다. 이 작업은 다음의 명령들을 통해 이루어질 수 있다:

xconsole$ mkdir trash
xconsole$ mv * trash
xconsole$ rm -rf trash
xconsole$ mkdir trash

위의 명령을 쉘 상에서 대화식으로 입력하는 대신, 쉘 프로그램을 작성하자:

#!/bin/bash
mkdir trash
mv * trash
rm -rf trash
mkdir trash
echo "Deleted all files!"

위의 내용을 clean.sh으로 저장하자. 이제 당신이 해야 할 일은 clean.sh를 실행하는 것뿐이다. clean.sh가 모든 파일을 trash디렉토리로 옮기고, trash 디렉토리를 삭제하고는 다시 만든 후, 모든 파일이 성공적으로 삭제되었다는 메시지까지 출력한다. 만일 당신이 명령을 입력하고 기다렸다가 다시 입력해야 하는 일을 해야 한다면, 쉘 프로그램으로 자동화하는 방법을 고려하라.

주석
주석을 달면 코드 읽기가 보다 쉬워진다. 주석을 단다고 해서 프로그램의 출력에 영향을 주지 않는다. 코드를 읽는 사람을 위해 주석은 특별히 만들어졌다. bash에서 첫번째 라인(#!/bin/bash)을 제외한 모든 주석은 해쉬 심벌("#")로 시작한다. 첫번째 라인은 주석이 아니다. 첫번째 라인 다음에 오는 "#"으로 시작하는 모든 라인은 주석이다. 다음의 코드를 보자:

#!/bin/bash
# 이 프로그램은 1부터 10까지 카운트한다:
for i in 1 2 3 4 5 6 7 8 9 10; do
    echo $i
done

만일 당신이 bash 스크립팅을 모른다고 해도, 주석 때문에 위의 프로그램이 무엇을 하는지 바로 알 수 있다. 주석을 적절하게 이용하는 것은 좋은 습관이다. 만일 훗날 당신의 프로그램을 유지 보수할 필요가 있다면, 주석을 달아 놓는 것이 일을 얼마나 편하게 만드는지 알게 될 것이다.

 

변수
변수는 기본적으로 값들을 담고 있는 "상자"이다. 당신은 많은 이유로 인해 변수들을 만들고 싶어할 것이다. 사용자 입력, 인자들, 또는 수치 값을 보관하기 위해 변수들이 필요하다. 다음 짧은 코드를 예로 들자:

#!/bin/bash
x=12
echo "The value of variable x is $x"

여기서 한 일은, x의 값을 12로 정한 것이다. echo "The value of variable x is $x" 이라는 라인은 x의 현재 값을 프린트한다. 변수를 정의할 때, 할당 연산자 "=" 사이에는 공백 문자가 있으면 안 된다. 여기 할당 연산자를 사용하는 문법이 있다:

variable_name=this_value

변수의 값은 변수 이름 앞에 달러 심벌"$"을 붙임으로써 얻을 수 있다. 위의 예제에서 보듯이, x의 값을 echo $x 를 사용해서 얻을 수 있다.

변수의 타입에는 지역 변수와 환경 변수, 이렇게 두 가지가 있다. 환경 변수들은 시스템에 의해 정해지고 주로 env 명령을 사용해서 볼 수 있다. 환경 변수는 특별한 값을 담고 있다. 예를 들어, 만일 다음과 같이 입력하면:

xconsole$ echo $SHELL
/bin/bash

현재 사용 중인 쉘의 이름을 얻을 수 있다. 환경 변수들은 /etc/profile과 ~/.bash_profile 안에 정의되어 있다. 환경 변수이든지, 지역 변수이든지 현재 변수 값을 체크할 때 echo 명령이 유용하게 쓰인다. 여전히 왜 변수가 필요한지 이해하지 못하겠다면, 여기 좋은 예제가 있다:

#!/bin/bash
echo "The value of x is 12."
echo "I have 12 pencils."
echo "He told me that the value of x is 12."
echo "I am 12 years old."
echo "How come the value of x is 12?"

이제 당신은 x의 값을 12 대신 8로 하기로 결정했다고 하자. 어떻게 하겠는가? "x is 12"라고 되어 있는 모든 라인을 바꿔야만 한다. 잠깐 기다려 보라…숫자 12를 가진 다른 라인들도 있다. 이 라인들도 바꿔야 하는가? 아니다. 그것들은 x와 관련이 없으니 그냥 둔다. 좀 복잡한가? 이제, 여기 변수를 사용한 것만 제외하면 똑 같은 예제가 있다:

#!/bin/bash
x=12     # 변수 x에 값 12를 대입한다
echo "The value of x is $x."
echo "I have 12 pencils."
echo "He told me that the value of x is $x."
echo "I am 12 years old." echo "How come the value of x is $x?"

여기서, 우리는 $x가 변수 x의 현재 값, 12를 프린트하는 것을 볼 수 있다. 그래서 만일 x의 값을 8로 바꾼다면, 당신이 해야하는 일은 x=12라고 되어 있는 라인을 x=8로 바꾸는 일뿐이다. 그러면 프로그램은 자동적으로 $x가 있는 라인을 12 대신 8로 바꾸어 보여 줄 것이다. 다른 라인은 바뀌지 않는다. 뒤에서 보겠지만, 변수는 다른 용도로도 중요하게 사용된다.

제어 구조
제어 구조는 당신의 프로그램을 보다 간결하게 하며, 프로그램이 결정을 내릴 수 있게 한다. 뿐만 아니라, 더욱 중요한 것은 에러를 체크하도록 할 수 있다는 것이다. 지금까지 우리가 본 예제는 모두 첫번째 라인에서부터 마지막 라인까지 프로그램의 모든 명령을 실행하는 프로그램이었다. 예를 들면 다음과 같은 방식이다:

#!/bin/bash
cp /etc/foo .
echo "Done."

이것은 bar.sh란 작은 프로그램인데, /etc/foo란 파일을 현재의 디렉토리로 복사하고는 화면에 "Done"이라고 프린트한다. 이 프로그램은 한 가지 조건 하에서 작동한다. /etc/foo라고 하는 파일이 반드시 존재해야만 한다. 그렇지 않다면 다음과 같은 화면을 보게 될 것이다:

xconsole$ ./bar.sh
cp: /etc/foo: No such file or directory
Done.

당신이 보듯이, 문제가 있다. 당신의 프로그램을 실행하는 모든 사람이 그들의 시스템에 /etc/foo라는 파일을 가지고 있지는 않다. 그래서 /etc/foo라는 파일이 있는지 체크해서 만일 그 파일이 있으면 복사하도록 하고, 그렇지 않다면 끝나도록 당신의 프로그램을 수정하는 편이 좋을 것이다. 의사 코드(pseudo code)로 이 내용을 나타내면 다음과 같다:

만일 /etc/foo가 존재하면,
    /etc/foo를 현재 디렉토리로 복사한다
    화면에 "Done."을 출력한다.
그렇지 않으면,
    화면에 "This file does not exist."을 촐력한다
    exit

이것을 bash에서 할 수 있을까? 물론! bash의 제어 구조에는 if, while, until, for, case가 있다. 각 구조는 시작을 나타내는 starting tag와 끝을 나타내는 ending tag로 쌍을 이루고 있다. 예를 들면, if 구조는 if로 시작해서 fi로 끝난다. 제어 구조는 당신의 시스템에서 발견되어지는 프로그램이 아니다. 그것들은 bash의 내재된 특성이다. 이러한 제어 구조를 이용해서, 단지 시스템의 프로그램만을 이용해서 쉘 프로그램을 만드는 것에 그치지 않고 당신 자신만의 고유한 코드를 작성할 수 있다.

if ... else ... elif ... fi
가장 널리 쓰이는 구조 중 하나가 if 구조이다. 이것을 이용해서 "만일 이 조건이 존재한다면 이것을 해라, 그렇지 않다면, 다른 것을 해라"라는 방식으로 프로그램이 결정을 내리도록 할 수 있다. 효율적으로 if 구조를 쓰기 위해서, 우리는 반드시 test 명령을 사용해야 한다. test는 파일의 존재 여부, 퍼미션 또는 유사점과 차이점을 체크한다. 여기 다시 작성한 bar.sh이 있다:

#!/bin/bash
if test -f /etc/foo
then
    # 파일이 존재하면, 복사하고 메시지를 출력한다.
    cp /etc/foo .
    echo "Done."
else
    # 파일이 존재하지 않으면, 메시지를 출력하고 프로그램을 종료한다.
    echo "This file does not exist."
    exit
fi

then과 else 다음 라인들을 들여 쓴 것에 주목하라. 들여쓰기는 선택 사항이지만, 그렇게 함으로써 어떤 조건 하에 어떤 라인이 실행될 것인지 눈에 잘 들어와서 코드를 훨씬 쉽게 읽을 수 있게 된다. 이제 프로그램을 실행하자. 만일 당신의 시스템에 /etc/foo 파일이 있다면, 프로그램은 파일을 복사한다. 그렇지 않다면, 에러 메시지를 출력할 것이다. test는 /etc/foo 파일이 존재하는지 체크한다. –f 옵션은  인자로 오는 것이 정규 파일인지 체크한다. 다음은 test의 옵션 리스트를 보여 주고 있다:

-d 파일이 디렉토리인지 체크
-e 파일이 존재하는지 체크
-f 파일이 일반적인 파일인지 체크
-g 파일이 SGID 퍼미션을 가졌는지 체크
-r 파일이 읽기 가능인지 체크
-s 파일의 크기가 0이 아닌지 체크
-u 파일이 SUID 퍼미션을 가졌는지 체크
-w 파일이 쓰기 가능인지 체크
-x 파일이 실행 가능인지 체크

else는 첫번째 조건이 만족되지 않아서 프로그램이 다른 일을 하도록 하고 싶을 때 사용된다. if문 안에 또 다른 if를 사용하고 싶을 때 사용할 수 있는 elif도 있다. 기본적으로 elif는 "else if"를 나타낸다. 첫번째 조건이 만족되지 않았고, 다른 조건을 테스트하고 싶을 때 elif를 사용한다.

 

만일 다음과 같은if와 test 구조 형식이 사용하기에 불편하다고 느껴진다면:

if test -f /etc/foo
then

다음과 같이 쓸 수 있다:

if [ -f /etc/foo ]; then

대괄호가 test 역할을 한다. 당신이 C 프로그래밍에 경험이 있다면, 이 문법이 보다 편안하게 느껴질 것이다. 양쪽 대괄호 사이에 공백 문자가 있어야 한다는 사실을 염두에 둬라. 세미콜론 ";"은 명령의 끝이라고 쉘에게 알려준다. 세미콜론 뒤에 오는 모든 것은 분리된 라인에 있는 것처럼 실행된다. 세미콜론을 사용함으로써 보다 읽기 쉬워진다. 물론 세미콜론을 사용하는 것은 선택 사항이다. 만일 괜찮다면, then을 다음 라인에 두자.

test에서 변수를 사용할 때, 따옴표로 변수를 둘러 싸서 사용하는 것은 좋은 생각이다. 다음의 예를 보자:

if [ "$name" -eq 5 ]; then

while ... do ... done
while 구조는 루프 구조이다. 기본적으로 while 구조가 하는 일을 말로 표현하면 다음과 같다. "이 조건이 참인 동안에는, 이것을 실행한다. 단, 조건이 더 이상 참이 아닐 때까지만". 이제 예제 하나를 보자:

#!/bin/bash
while true; do
   echo "Press CTRL-C to quit."
done

true는 실제 하나의 프로그램이다. 이 프로그램이 하는 일은 중단없이 루프가 계속 돌도록 하는 것이다. while 구조에서 true를 사용할 때는 쉘 프로그램이 true를 부른 다음 실행해야 하므로 아무래도 속도면에서 느릴 수 밖에 없다. 하지만 true의 대신 ":" 명령을 사용할 수 있다.

#!/bin/bash
while :; do
   echo "Press CTRL-C to quit."
done

이것은 정확하게 같은 결과를 보여주지만, ":" 명령이 bash 안에 내포된 특성이기 때문에 훨씬 빠르다. 위의 예제가 이전 예제와 다른 점은 읽기가 조금 어려워진 대신 속도가 빨라졌다는 것이다. true와 ":" 명령 중 더 편하게 느껴지는 것을 사용하라. 다음에 제시되는 변수를 사용하는 예제는 아마도 보다 더 유용할 것이다:

#!/bin/bash
x=0;     # x의 값을 0으로 초기화
while [ "$x" -le 10 ]; do
    echo "Current value of x: $x"
    # x의 값을 증가시킨다:
    x=$(expr $x + 1)
    sleep 1
done

위의 예제에서 보듯이, 여기서는 test(대괄호 안의 폼)를 이용해서 변수 x의 조건을 체크하고 있다. 옵션 –le는 만일 x가 값 10과 같거나 작은지 체크한다. 위의 코드를 말로 하면, " x가 10 보다 작거나 같은 동안, x의 현재 값을 프린트하고는 x의 현재 값에 1을 더한다"이다. sleep 1 은 단지 프로그램이 잠깐 쉬도록 한다. 이 문장은 없어도 상관없다. 이 예제에서 하는 일은 상등(equality) 테스트이다. 만일 변수가 어떤 값과 같은지 체크해서 같다면 그에 해당하는 일을 하는 것이다. 여기서 상등 테스트들의 리스트를 보도록 하자:

숫자들 사이의 상등 체크:
x -eq y   x가 y와 같은지 체크
x -ne y   x가 y와 같지 않은지 체크
x -gt y   x가 y 보다 큰지 체크
x -lt y   x가 y 보다 작은지 체크

문자열 사이의 상등 체크:
x = y   문자열 x가 문자열 y와 같은지 체크
x != y  문자열 x가 문자열 y와 다른지 체크
-n x   문자열 x가 널 문자가 아니면 true로 간주함
-z x   문자열 x가 널 문자이면 true로 간주함.

위에서 우리가 작성한 루프 스크립트는 다음의 한 라인을 제외하면 이해하기에 어렵지 않다:

x=$(expr $x + 1)

위의 명령은 x의 값을 1만큼씩 증가시킨다고 알려주고 있다. 하지만 $(...)가 뜻하는 것은 무엇인가? 변수인가? 아니다. 사실, 이것은 당신이 명령 expr $x + 1 를 먼저 실행하고, 그 결과를 다시 x 값으로 하기 원한다고 쉘에게 알려주는 방법이다. 어떤  명령이든 $(...)으로 둘러 싸인 명령은 먼저 실행된다:

#!/bin/bash
me=$(whoami)
echo "I am $me."

이 예제를 실행해보면 내가 말하는 바를 이해하게 될 것이다. 위의 예제는 다음과 같이 쓰여질 수도 있는데 결과는 같다:

#!/bin/bash
echo "I am $(whoami)."

어느 쪽이 보다 읽기 쉬운지는 당신이 결정하라. 명령을 실행하거나 명령의 결과를 변수에 대입하는 다른 방법이 또 있다. 이것은 뒤에서 설명하도록 하겠다. 지금은 $(...)을 사용하자.

until ... do ... done
until 구조는 while 구조와 매우 유사하다. 단 하나의 차이점은 조건이 반대라는 점이다. while 구조는 조건이 참인 동안은 계속해서 반복되지만 until 구조는 조건이 참이 될 때까지 반복된다. 그래서 기본적으로 until 구조는 "이 조건이 참이 될 때까지, 이것을 해라"라는 구조이다. 다음 예제 하나를 보도록 하자:

#!/bin/bash
x=0
until [ "$x" -ge 10 ]; do
    echo "Current value of x: $x"
    x=$(expr $x + 1)
    sleep 1
done

아마도 이 짧은 코드는 어디서 본 듯할 것이다. 한번 실행해서 결과가 어떻게 나오는지 보라. 기본적으로, until은 x가 10 보다 크거나 같게 될 때까지 계속해서 반복할 것이다. x의 값이 10에 이르게 되면, 루프는 정지할 것이다. 따라서, 마지막에 프린트 되는 x의 값은 9가 될 것이다.

for ... in ... do ... done
for 구조는 일정 범위의 변수 안에서 루프를 돌 때 사용된다. 예를 들자면, 매 초마다 10개의 점을 프린트하는 작은 프로그램을 작성할 때 for를 사용할 수 있다:

#!/bin/bash
echo -n "Checking system for errors"
for dots in 1 2 3 4 5 6 7 8 9 10; do
    echo -n "."
    echo "System clean."
done

모르는 사람도 있을 수 있으므로, -n 옵션에 대해 설명하자. -n 옵션은 echo  사용 시 자동적으로 개행 문자가 더해지지 않도록 하기 위해 사용된다. 한번은 –n 옵션을 가지고, 또 한번은 없이 실행해보면 내가 말하는 것이 무엇인지 알 수 있을 것이다. 변수 dots은 1부터 10까지의 값을 갖고 루프를 돌면서 각 값마다 점 하나씩을 프린트한다. 변수가 값을 갖고 루프를 돈다는 것이 무엇을 뜻하는지 보기 위해 다음의 예제를 해보자:

#!/bin/bash
for x in paper pencil pen; do
    echo "The value of variable x is: $x"
    sleep 1
done

프로그램을 실행할 때, 맨 처음엔 x가 paper란 값을 갖고, 다음엔 pencil, 그 다음엔 pen이란 값을 갖는 것을 볼 수 있다. 더 이상의 값이 없을 때, 루프는 끝난다.

여기 좀 더 유용한 예제가 있다. 다음 프로그램은 현재 디렉토리에 있는 모든 파일에 .html 확장자를 붙인다:

#!/bin/bash
for file in *; do
    echo "Adding .html extension to $file..."
    mv $file $file.html
    sleep 1
done

*는 와일드 카드 문자이다. *가 뜻하는 것은 "현재 디렉토리의 모든 것"으로 이 예제에서는 현재 디렉토리의 모든 파일을 뜻한다. 이 프로그램을 실행하면 현재 디렉토리의 모든 파일 뒤에 .html 확장자가 붙게 된다. 변수 file이 모든 값을 가지면서 루프를 돈다는 것을 상기하라. 이 경우에는 현재 디렉토리의 파일들을 값으로 갖는다. mv는 변수 file의 값을 .html 확장자를 가진 이름으로 수정하는데 사용된다.

case ... in ... done
case 구조는 if 구조와 매우 유사하다. 기본적으로 체크 해야할 조건이 많고 if 문을 계속해서 쓰고 싶지 않을 때 case 구조는 그 위력을 발휘한다. 다음의 짧은 코드를 보자:

#!/bin/bash
x=5     # x 값을 5로 초기화
# 이제 x의 값을 체크한다:
case $x in
   0) echo "Value of x is 0."
      ;;
   5) echo "Value of x is 5."
      ;;
   9) echo "Value of x is 9."
      ;;
   *) echo "Unrecognized value."
esac

case 구조는 x의 값에 대해서 세 가지의 가능성을 체크할 것이다. 이 예제에서는 처음에 x의 값이 0인지 체크 한 후, 그 값이 5인지 체크하고 다음에는 9인지 체크한다. 마지막으로 앞의 모든 경우에 해당하지 않는다면, "Unrecognized value."라는 메시지를 출력한다. "*"가 "모든 것"을 뜻한다고 했던 것을 기억하라. 이 예제에서는 "앞서 명시되었던 값이 아닌 모든 값"을 뜻한다. 만일 x의 값이 0, 5, 9가 아닌 다른 값이라면 그 값은 *의 범주에 들어가게 된다. case를 사용할 때, 각 조건은 반드시 두 개의 세미콜론으로 끝나야 한다. if를 쓸 수 있는데도 왜 case를 사용하는가? 여기 if를 사용해서 작성한 위의 예제와 똑 같은 프로그램이 있다. 어느 쪽이 작성하기 쉬우며 읽기 쉬운지 비교해 보라:

#!/bin/bash
x=5     # x의 값을 5로 초기화
if [ "$x" -eq 0 ]; then
    echo "Value of x is 0."
elif [ "$x" -eq 5 ]; then
    echo "Value of x is 5."
elif [ "$x" -eq 9 ]; then
    echo "Value of x is 9."
else
    echo "Unrecognized value."
fi

인용 부호
쉘 스크립팅에서 인용 부호는 중요한 부분을 차지한다. 인용 부호에는 세 가지 종류가 있다. 따옴표: ", 작은 따옴표(어포스트로피): ', 역 따옴표(억음 악센트라고도 함): `의 세 가지이다. 이들 각각이 의미하는 것이 다른가? 그렇다.

따옴표는 주로 공백 문자를 포함한 문자열을 담을 때 이용된다. 예를 들면, "This string contains whitespace."을 보자. 따옴표로 둘러 싸인 문자열은 하나의 인자로서 취급된다. 다음의 예제를 보자:

xconsole$ mkdir hello world
xconsole$ ls -F
hello/     world/

이 예제에서 우리는 두 개의 디렉토리를 만들었다. mkdir은 hello와  world를 두 개의 인자로 받아들여서 두 개의 디렉토리를 만든 것이다. 이제, 다음과 같이 했을 때의 결과는 어떤지 살펴 보도록 하자:

xconsole$ mkdir "hello world"
xconsole$ ls -F
hello/     hello world/     world/

이번에는 이름이 두 단어로 이루어진 디렉토리를 만들었다. 따옴표가 두 단어를 하나의 인자로 만든 것이다. 따옴표가 없다면, mkdir은 hello를 첫번째 인자로, world를 두번째 인자로 생각할 것이다.

작은 따옴표는 주로 변수를 다룰 때에 사용된다. 만일 변수가 따옴표로 둘러싸여 있으면 그 값의 수치가 구해질 것이다. 만일 작은 따옴표로 둘러싸여 있으면 그 값의 수치는 계산되어지지 않는다. 이것이 뜻하는 바를 명확히 하기 위해 다음의 예제를 실행해보자:

#!/bin/bash
x=5     # initialize x to 5
# use double quotes
echo "Using double quotes, the value of x is: $x"
# use forward quotes
echo 'Using forward quotes, the value of x is: $x'

무엇이 차이 나는지 보았는가? 만일 문자열을 변수로 사용할 계획이 아니라면 따옴표로 둘러싸서 사용할 수 있다. 하지만 어떻게 할 지 정하지 못했다면 작은 따옴표로도 따옴표처럼 문자열에 공백 문자를 포함하도록 사용할 수 있다:

xconsole$ mkdir 'hello world'
xconsole$ ls -F
hello world/

역 따옴표는 따옴표나 작은 따옴표와는 완전히 다른 용도로 쓰인다. 공백 문자를 포함하는 데에는 쓰이지 않는다. 이 글의 앞쪽에서 다음 라인을 사용했던 것을 기억해 보라:

x=$(expr $x + 1)

이미 당신이 알듯이, 명령 expr $x + 1의 결과가 변수 x에 들어가게 된다. 역 따옴표를 사용한 다음의 명령도 꼭 같은 결과를 낸다:

x=`expr $x + 1`

어느 것을 사용해야 하는가 하는 문제는 순전히 당신에게 달려 있다. 당신이 좋은 쪽을 선택하라. 역 따옴표가 $(...) 보다 자주 사용되는 것을 볼 수 있을 것이다. 하지만, $(...)가 보다 읽기 쉽다. 다음과 같은 경우엔 특히 더 그렇다:

$!/bin/bash
echo "I am `whoami`"

 

BASH에서의 수식 연산
bash 은 수식 표현을 수행할 수 있도록 해준다. 이미 보았듯이, 수식 연산은 expr 명령을 통해서 수행된다. 그러나, expr은 true 명령과 같이 느린 것으로 생각된다. 이들 명령이 느린 이유는 이들 명령을 수행하기 위해, 쉘은 그것들을 시작해야 한다. 쉘 자체의 내포된 특성을 이용하는 것이 훨씬 더 빠르다. 그래서 true 대신에 ":"을 사용하는 것을 앞서서 보았다. expr을 사용하는 대신, 계산하고자 하는 수식을 $((...)) 안에 쓰면 된다. 이것은 $(...)과는 다르다. 괄호의 숫자가 다르지 않은가. 다음 예제를 실행해 보자:

#!/bin/bash
x=8     # x의 값을 8로 초기화
y=4     # y의 값을 4로 초기화
# 이제 x와 y의 합을 z에 대입한다:
z=$(($x + $y))
echo "The sum of $x + $y is $z"

어떤 것을 선택하든지, 순전히 당신에게 달려 있다. 만일 당신이 $((...))을 사용하는 것보다 expr을 사용하는 것이 편하다면, 그것을 사용하면 된다.

bash에서는 덧셈, 뺄셈, 곱셈, 나눗셈, 모듈러스 계산이 가능하다. 각 계산이 그에 해당하는 연산자를 가지고 있다:

계산              연산자
덧셈                             +
뺄셈                             -
곱셈                             *
나눗셈                          /
모듈러스                     %

처음의 네 개 연산자는 모든 사람들에게 익숙할 것이다. 마지막에 있는 모듈러스는 두 개의 값으로 나눗셈을 할 때 나머지 값을 말하는 것이다. 다음은 bash에서의 수식 연산에 대한 예제이다:

#!/bin/bash
x=5   # x의 값을 5로 초기화
y=3   # y의 값을 3로 초기화

add=$(($x + $y))   # x와 y 값을 더해서 변수add에 대입
sub=$(($x - $y))   # x의 값에서 y의 값을 빼서 변수 sub에 대입
mul=$(($x * $y))   # x와 y 값을 곱해서 변수 mul에 대입
div=$(($x / $y))   # x의 값을 y의 값으로 나누어 변수div에 대입
mod=$(($x % $y))   # x / y의 나머지 값을 구해 변수 mod에 대입

# 답을 출력:
echo "Sum: $add"
echo "Difference: $sub"
echo "Product: $mul"
echo "Quotient: $div"
echo "Remainder: $mod"

위의 예제 코드는 expr을 사용해서 다시 쓸 수도 있다. 예를 들면, add=$(($x + $y)) 대신에 add=$(expr $x + $y)이나, add=`expr $x + $y`를 사용할 수 있다.

사용자 입력 읽기
이제부터 재미있는 부분이 시작된다. 당신의 프로그램이 사용자와 대화하도록 만들수 있다. 사용자로부터 입력을 받아들이는 명령은 read이다. read는 변수를 사용하는 bash에 내포된 명령이다. 다음의 예를 보자:

#!/bin/bash
# 사용자 이름을 받아들이고 인사를 출력한다
echo -n "Enter your name: "
read user_name
echo "Hello $user_name!"

이 예제에서 변수는 user_name이다. 물론 변수 이름은 당신이 좋아하는 다른 것으로 불러도 된다. read 는 사용자가 무엇인가 입력하고는 엔터키를 누르기를 기다린다. 만일 아무 것도 입력되지 않고 엔터키가 눌러지면, read 는 코드의 다음 라인을 실행한다. 한번 해보라. 여기 사용자가 무엇인가 입력했는지 확인하기 위해 체크하는 것만 제외하고는 위의 예제와 꼭 같은 코드가 있다:

#!/bin/bash
# 사용자 이름을 받아들이고 인사를 출력한다
echo -n "Enter your name: "
read user_name

# 사용자가 아무 것도 입력하지 않으면:
if [ -z "$user_name" ]; then
    echo "You did not tell me your name!"
    exit
fi

echo "Hello $user_name!"

여기에서, 만일 사용자가 아무 것도 입력하지 않고 엔터키를 누른다면, 우리의 프로그램은 불평을 하고는 끝나 버릴 것이다. 사용자 입력을 받아들이는 것은 사용자가 특정한 것을 입력하도록 하는 대화식 프로그램에 유용하게 쓰인다. 예를 들면, 간단한 데이터베이스를 만들고 사용자에게 데이터베이스에 들어 갈 내용을 입력하도록 할 수 있을 것이다.

함수
함수를 이용하면 스크립팅은 더욱 쉬워지고 코드는 유지 보수하기가 쉬워진다. 기본적으로 함수는 프로그램을 작은 조각으로 나눈다. 함수는 당신이 정의한 일을 수행하고 만일 당신이 원한다면 어떤 값을 리턴할 수도 있다. 함수에 관한 내용을 계속해서 설명하기 전에, 함수를 이용한 쉘 프로그램의 예를 하나 보기로 하자:

#!/bin/bash

# 함수 hello() 는 단지 메시지를 프린트하기만 한다
hello()
{
    echo "You are in function hello()"
}

echo "Calling function hello()..."
# 함수 hello() 를 부른다:
hello
echo "You are now out of function hello()"

위의 예제를 실행해 보아라. 함수 hello() 는 여기서는 메시지를 프린트하는 단 하나의 목적을 가지고 있다. 물론 함수는 보다 복잡한 작업을 하도록 만들 수 있다. 위의 예에서, 우리는 함수 hello()를 다음과 같이 이름을 이용해서 불렀다:

hello

이 라인이 실행되었을 때, bash는 hello()가 있는 라인을 찾는다. 그 라인을 맨 처음에서 찾자마자, hello() 함수의 내용을 실행한다.

위에서 봤듯이, 함수는 항상 그것의 이름으로 불리워진다. 함수를 작성할 때는 위에서 했듯이 function_name()으로 시작하거나, 함수라는 것을 명시하고 싶다면 function function_name()으로 시작할 수 있다. 함수 hello()를 다음과 같이 작성할 수도 있다:

function hello()
{
    echo "You are in function hello()"
}

함수는 항상 빈 괄호"()"를 가지고 시작해서는 그 뒤에 시작과 끝을 나타내는 중괄호"{...}"가 온다. 이 중괄호는 함수의 시작과 끝을 알려준다. 중괄호로 둘러싸인 모든 코드는 함수가 불려지면 실행될 것이고 오직 그 함수에만 속한다. 함수는 항상 불려지기 이전에 정의되어야 한다. 다음의 예제는 위의 예제 프로그램의 순서를 바꿔서 함수가 정의되기 전에 부르도록 하였다

#!/bin/bash
echo "Calling function hello()..."
# 함수hello() 를 부른다:
hello echo "You are now out of function hello()"

# function hello() just prints a message
hello()
{
    echo "You are in function hello()"
}

위의 예제를 실행한 결과는 다음과 같다:

xconsole$ ./hello.sh
Calling function hello()...
./hello.sh: hello: command not found
You are now out of function hello()

당신이 보듯이, 에러가 발생했다. 그러므로, 항상 코드를 작성할 때는 함수를 처음에 정의하라. 적어도 함수가 불려지기 전에 정의하라. 여기 함수를 이용하는 또 다른 예가 있다:

#!/bin/bash
# admin.sh – 관리 도구

# 함수 new_user() 는 새로운 사용자 계정을 만든다
new_user()
{
    echo "Preparing to add a new user..."
    sleep 2
    adduser     # run the adduser program
}

echo "1. Add user"
echo "2. Exit"

echo "Enter your choice: "
read choice

case $choice in
    1) adduser     # 함수 adduser()를 부른다
       ;;
    *) exit
       ;;
esac

이 예제가 제대로 실행되기 위해서는, 당신은 루트 사용자이어야 한다. adduser 가 오직 루트만 실행할 수 있는 프로그램이기 때문이다. 짤막한 이 예제로 함수가 얼마나 유용하게 쓰일 수 있는지 당신이 알 수 있으면 좋겠다.

시그널 이용하기
당신의 프로그램에서 시그널을 붙잡아 이용하기 위해서 내포된 명령인 trap을 쓸 수 있다. 이것은 프로그램이 실행되고 있는 도중에 갑자기 아무런 메시지도 없이 끝나 버리는 일없이 우아하게 종료시킬 수 있는 좋은 방법이다. 예를 들어 보자. 만일 당신이 프로그램을 실행하고 있는 중이라면, CTRL-C를 누르는 것은 프로그램에게 인터럽트(interrupt) 시그널을 보낸다. 인터럽트 시그널은 프로그램을 강제로 종료 시킨다. trap 명령을 사용하면 이 시그널을 잡아내서 프로그램을 계속할 것인지 아니면 사용자에게 프로그램을 종료한다는 메시지를 보내도록 하든지 하는 선택을 할 수 있다. trap은 다음의 문법으로 사용된다:

trap action signal

action 은 시그널을 잡아 냈을 때 당신이 수행하고 싶은 일이고 signal은 잡아 내고자 하는 시그널이다. 시그널의 리스트는 trap –l 명령으로 볼 수 있다. 당신의 쉘 프로그램에서 시그널을 이용할 때, 시그널의 처음 세 글자, 보통은 SIG를 생략한다. 예를 들면, 인터럽트 시그널은 SIGINT이지만 다음에 보게 될 예제에서는 INT만을 이용한다. 시그널 이름에 덧붙여진 시그널 번호를 이용해도 된다. 예를 들어, SIGINT의 수치 값은 2이다. 다음의 프로그램을 실행해 보자:

#!/bin/bash
# trap 명령을 이용하기

# CTRL-C 를 붙잡아고 함수 sorry()를 실행:
trap sorry INT

# 함수 sorry()는 메시지를 출력한다
sorry()
{
    echo "I'm sorry Dave. I can't do that."
    sleep 3
}

# 10부터 1까지 카운트:
for i in 10 9 8 7 6 5 4 3 2 1; do
    $i seconds until system failure."
    sleep 1
done
echo "System failure."

이제, 프로그램이 실행되어 카운트 다운을 하고 있는 동안에, CTRL-C을 누르자. 그러면 프로그램에 인터럽트 시그널이 보내질 것이다. 하지만, 그 시그널은 trap 명령에 의해 붙잡히고, trap 명령은 sorry() 함수를 실행할 것이다. action 자리에 "''" 을 둠으로써 trap이 시그널을 무시하도록 할 수 있다. 또한 "-"를 써서trap을 리셋할 수도 있다. 그 예를 보자:

# 만일 SIGINT 시그널이 붙잡히면 함수sorry()를 실행한다:
trap sorry INT

# trap을 리셋한다:
trap - INT

# SIGINT가 붙잡혀도 아무 것도 하지 않도록 한다:
trap '' INT

trap을 리셋하면 프로그램을 인터럽트하고 강제로 종료하는 원래의 작업이 시행된다. trap이 아무 것도 하지 않도록 하면 들어온 시그널을 무시하고 프로그램은 계속 실행된다.

AND 와 OR
앞에서 제어 구조의 사용과 그 유용성에 대해 살펴 보았다. 거기에 더해질 수 있는 두 가지 내용이 더 있다. 바로 AND "&&"와 OR "||" 구문이다. AND 구문은 다음과 같다:

조건_1 && 조건_2

AND 구문은 처음에 가장 왼쪽에 있는 조건을 체크한다. 만일 참이라면, 두 번째 조건을 체크한다. 두 번째 조건도 참이라면, 나머지 코드가 실행된다. 만일 조건_1이 거짓이라면 조건_2는 실행되지 않는다. 말로 풀어 쓰면:

만일 조건_1이 참이면, 그리고 만일 조건_2가 참이면, 그렇다면...

AND 구문을 사용하는 예제를 보도록 하자:

#!/bin/bash
x=5
y=10
if [ "$x" -eq 5 ] && [ "$y" -eq 10 ]; then
    echo "Both conditions are true."
else
    echo "The conditions are not true."
fi

여기서, x와 y, 두 변수 모두 우리가 체크하는 값들을 가지고 있으므로 조건들이 참이 된다. 만일 값을 x=5를 x=12로 바꾸고 다시 프로그램을 실행시키면, 이제는 조건이 거짓이 된다.

OR 구문도 비슷한 방식으로 사용된다. 단 한 가지 차이점은 가장 왼쪽의 조건이 거짓인지 체크한 다음에 그 다음 조건을 체크한다는 사실이다:

조건_1 || 조건_2

의사 코드로 이것을 번역해 보면 다음과 같다:

만일 조건_1이 참이라면, 또는 조건_2가 참이라면, 그렇다면...

그렇기 때문에, 테스트되는 조건 중 어느 것 하나라도 참이면 뒤에 오는 코드는 모두 실행된다:

#!/bin/bash
x=3
y=2
if [ "$x" -eq 5 ] || [ "$y" -eq 2 ]; then
    echo "One of the conditions is true."
else
    echo "None of the conditions are true."
fi

위의 예제에서, 두 개의 조건 중 하나만이 참이라는 것을 알 수 있을 것이다. 하지만, y의 값을 다른 것으로 바꾸고 다시 실행시키면 두 조건 모두 참이 아니라는 것을 알 수 있을 것이다.

AND와 OR 구문 대신에 if 구조를 쓸 수도 있다. 하지만 그렇게 하면 중첩된(nesting) if 문장들을 쓰게 된다. 중첩되었다는 것(nesting)은 if 구조 안에 또 다른 if 구조가 있는 것을 말한다. 물론 다른 제어 구조를 중첩해서 쓰는 것도 가능하다. 다음은 앞선 AND 코드와 같은 내용을 중첩된 if 구조를 이용해서 다시 작성한 것이다:

#!/bin/bash
x=5
y=10
if [ "$x" -eq 5 ]; then
    if [ "$y" -eq 10 ]; then
        echo "Both conditions are true."
    else
        echo "The conditions are not true."
    fi
fi

이 코드는 AND 구문을 사용하는 것과 같은 목적으로 작성된 것이지만 AND 구문보다 읽기가 어렵고 작성하는데 보다 많은 시간이 걸린다. AND와 OR 구문을 이용하는 것이 더 현명하다.

인자(argument) 사용하기
리눅스 프로그램의 대부분이 비대화식이라는 것을 알고 있을 것이다. 프로그램들은 인자를 입력하도록 요구하고, 만일 인자를 입력하지 않으면 "usage" 메시지를 출력한다. more 명령을 예로 들자. 만일 more 명령 다음에 파일 이름을 입력하지 않으면, 바로 "usage" 메시지를 내보낸다. 당신의 쉘 프로그램이 인자들에 대해 일하도록 하는 것은 가능하다. 그렇게 하기 위해서, "$#" 변수를 알아야만 한다. 이 변수는 프로그램에 넘겨지는 인자들의 총 개수를 나타낸다. 예를 들어, 다음의 프로그램을 실행시킨다면:

xconsole$ foo argument

$#는 1이라는 값을 가질 것이다. 프로그램 foo에 넘겨지는 인자가 하나뿐이기 때문이다. 만일 인자가 두 개라면, $#는 2라는 값을 가질 것이다. 뿐만 아니라, 명령 라인의 각 단어들, 즉, 프로그램 이름(이 경우엔, foo)과 인자들은 쉘 프로그램 안에서 변수로 사용될 수 있다. foo는 $0이 되고 argument는 $1이 된다. 변수는 최고 9개까지 가능한데, 프로그램 이름인 $0과 그 뒤의 인자 하나 하나에 대응되는 $1부터 $9까지를 변수로 가질 수 있다. 다음의 예제를 보도록 하자:

#!/bin/bash
# 첫번째 인자를 출력한다
# 처음에 인자를 갖는지 체크한다:
if [ "$#" -ne 1 ]; then
    echo "usage: $0 "
fi

echo "The argument is $1"

이 프로그램을 실행하기 위해서는 오직 한 개의 인자를 필요로 한다. 만일 하나보다 작거나 많은 인자를 입력하면 프로그램은 usage 메시지를 출력할 것이다. 그렇지 않고 한 개의 인자를 프로그램에 건네면, 이 쉘 프로그램은 당신이 건넨 인자를 출력할 것이다. $0가 프로그램의 이름이므로 "usage" 메시지에 사용된다. 마지막 라인에서 $1을 사용하고 있다. $1에 프로그램에 넘겨지는 인자의 값이 들어 있다는 것을 기억하라.

출력 재지정과 파이프
일반적으로, 당신이 명령을 실행할 때, 그 출력은 화면에 나타난다. 예를 들면:

xconsole$ echo "Hello World"
Hello World

출력 재지정(redirection)은 출력 방향을 다른 곳(대부분은 파일)으로 바꿀 수 있게 해 준다. ">" 연산자는 출력의 방향을 재지정할 때 사용된다. 화살표를 출력 내용이 가야할 곳으로 생각하면 이해하기 쉽다. 여기 출력을 파일로 지정한 예제가 있다:

xconsole$ echo "Hello World"> foo.file
xconsole$ cat foo.file
Hello World

이 예제에서, echo "Hello World"의 출력은 foo.file란 이름의 파일로 재지정되었다. 출력 내용이 파일의 내용으로 된 것을 볼 수 있다. ">" 연산자의 한 가지 문제점은 파일의 내용을 겹쳐 쓴다는 것이다. 만일 파일 내용을 겹쳐 쓰지 않고 뒤에 덧붙이고 싶다면 어떻게 하면 될까? 파일 뒤에 덧붙이기 위해서는 반드시 ">>" 연산자를 써야 한다. 이 연산자는 파일 내용을 겹쳐 쓰지 않고 뒤에 덧붙인다는 점을 제외하면 출력 재지정 연산자와 꼭 같다.

마지막으로, 파이프에 대해 이야기 하자. 파이프는 프로그램으로부터 나오는 출력 결과를 가져와서, 다른 프로그램의 입력으로 사용할 수 있게 한다. 파이프를 이용하기 위해서는 파이프 연산자: "|"를 쓰면 된다. "|"는 영문자 "L"의 소문자가 아니다. 파이프 연산자는 SHIFT-\를 이용해서 얻을 수 있다. 이제 파이프에 관한 예제를 보자:

xconsole$ cat /etc/passwd | grep xconsole
xconsole:x:1002:100:X_console,,,:/home/xconsole:/bin/bash

이 예제에서 우리는 /etc/passwd 파일 전체를 읽은 뒤, 파이프를 이용해서 그 출력을 입력 내용 중 xconsole이란 문자열을 찾는grep 명령의 입력으로 사용하여 그 문자열을 포함한 라인을 화면에 출력했다. 마지막 출력을 파일에 저장하기 위해 출력 재지정을 함께 쓸 수도 있다:

xconsole$ cat /etc/passwd | grep xconsole > foo.file
xconsole$ cat foo.file
xconsole:x:1002:100:X_console,,,:/home/xconsole:/bin/bash

제대로 작동하고 있다. /etc/passwd 파일을 읽은 뒤, 전체 출력 내용을 파이프를 통해서 문자열xconsole 을 찾는 grep 명령의 입력으로 사용하고는 최종 출력 결과를 리다이렉션해서 foo.file 에 저장했다. 쉘 프로그램을 작성할 때 출력 재지정과  파이프가 유용한 도구로 쓰인다는 것을 알게 될 것이다.

임시 파일
때때로 임시 파일을 만들어야 할 필요가 있을 때가 있다. 이 파일은 임시적으로 어떤 데이터를 갖고 있거나, 또는 단지 어떤 프로그램과 함께 작동할 수도 있다. 일단 프로그램의 목적이 달성되면, 대부분의 임시 파일은 삭제된다. 파일을 만들 때는 파일의 이름을 주어야만 한다. 파일을 만들 때의 문제는 새로 만드는 파일 이름과 같은 이름의 파일이 같은 디렉토리 안에 있으면 안된다는 것이다. 만일 그렇지 않게 되면, 중요한 데이터를 겹쳐 쓸 수도 있다. 하나뿐인 이름의 임시 파일을 만들기 위해, "$$" 기호를 파일 이름의 첫머리나 끝에 붙일 필요가 있다. 예를 들어, hello라는 이름을 가진 임시 파일을 만들고 싶다고 하자. 당신의 프로그램을 실행시키는 사용자 또한 hello라는 파일을 가질 수 있다고 하면 당신의 프로그램에서 사용하는 임시 파일과 충돌이 일어날 수 있다. hello라는 파일 대신에 hello.$$ 또는 $$hello라는 이름의 파일을 만들어서 단 하나뿐인 파일을 만들 수 있다. 다음을 실행해 보자:

xconsole$ touch hello
xconsole$ ls
hello
xconsole$ touch hello.$$
xconsole$ ls
hello     hello.689

당신의 임시 파일이 있는 것을 볼 수 있다.

리턴 값
대부분의 프로그램은 프로그램을 어떻게 나가느냐에 따라 특정한 값을 리턴한다. 예를 들면, grep의 매뉴얼 페이지를 보면 찾고자 하는 문자열을 발견하면 grep은 0을 리턴하고 그렇지 않으면 1을 리턴한다고 나와 있다. 왜 프로그램의 리턴 값에 대해 신경을 써야 하는 것인가? 여러 가지 이유에서이다. 당신이 특정 사용자가 시스템에 있는지 체크하고 싶다고 하자. 이것을 체크해 볼 방법 중의 하나는 /etc/passwd 파일 안에 사용자의 이름이 있는지 grep 명령을 통해 찾아 보는 것이다. 찾고자 하는 사용자 이름이 foobar라고 하자:

xconsole$ grep "foobar" /etc/passwd
xconsole$

아무 것도 출력되지 않았다. 이것이 뜻하는 것은 grep 명령이 찾고자 하는 문자열과 일치하는 것을 찾지 못했다는 것이다. 만일 일치하는 문자열을 찾지 못했다고 메시지를 출력하면 훨씬 더 도움이 될 것이다. 이 때가 바로 프로그램의 리턴 값을 얻을 필요가 있는 때이다. 프로그램의 리턴 값은 특별한 변수가 가지고 있다. 그 변수는 $?이다. 다음의 짧은 코드를 보도록 하자:

#!/bin/bash
# 사용자 foobar 를 찾고 모든 출력을 파이프를 통해 /dev/null로 보낸다:
grep "foobar" > /dev/null 2>&1
# 리턴 값을 받아서 그에 해당하는 코드를 실행한다:
if [ "$?" -eq 0 ]; then
   echo "Match found."
    exit
else
    echo "No match found."
fi

이제 프로그램을 실행하면, grep의 리턴 값을 붙잡을 것이다. 만일 그 값이 0과 같다면, 일치하는 문자열이 발견된 것이어서 그에 해당하는 메시지가 출력된다. 그렇지 않으면, 일치하는 문자열이 없다고 메시지를 출력한다. 이것은 프로그램의 리턴 값을 받아 이용하는 가장 기초적인 예제이다. 계속해서 연습하다 보면, 당신이 원하는 것을 하기 위해서 프로그램의 리턴 값이 필요한 때가 많을 것이다.

그렇다면 프로그램이 종료되는 상황에 따라 당신의 쉘 스크립트가 특정한 값을 리턴하게 하고 싶다면 어떻게 해야 하는가? exit 명령은 리턴할 값을 하나의 인자로서 가진다. 일반적으로 숫자 0은 성공적인 종료, 에러가 하나도 없이 프로그램이 끝났음을 가리킨다. 일반적으로 0보다 크거나 작은 숫자는 에러가 생겼음을 뜻한다. 리턴 값을 정하는 문제는 프로그래머가 결정할 문제이다. 다음의 프로그램을 보자:

#!/bin/bash
if [ -f "/etc/passwd" ]; then
    echo "Password file exists."
    exit 0
else
    echo "No such file."
    exit 1
fi

exit에 리턴 값을 명시하였으므로, 이 스크립트를 이용하는 다른 쉘 스크립트에서는 이 스크립트의 리턴 값을 붙잡아 사용할 수 있다.

수치 값인 인자 하나를 갖는 return 명령을 이용해서, 함수들도 값을 리턴할 수 있다. 함수에 적용된다는 점을 제외하면 exit가 사용되는 방식과 똑 같은 방법으로 return을 이용할 수 있다. 다음 예를 보자:

check_passwd()
{
    # passwd 파일이 존재하는지 체크:
    if [ -f "/etc/passwd" ]; then
       echo "Password file exists."
        # 존재하면 0을 리턴한다:
       return 0
   else
        # 존재하지 않는다면 1을 리턴한다:
       echo "No such file."
       return 1
   fi
}

# 함수 check_passwd()로부터 리턴 값을 얻는다:
foo=check_passwd
# 값을 체크한다:
if [ "$foo" -eq 0 ]; then
    echo "File exists."
    exit 0
else
    echo "No such file."
    exit 1
fi

코드를 자세히 보라. 이해하기 어렵지 않다. 함수 check_passwd()의 리턴 값을 가지는 foo라는 이름의 변수를 가지고 시작한다. 함수 check_passwd()에서 우리는 /etc/passwd 파일이 존재하는지 체크한다. 만일 존재한다면, 0을 리턴하고, 그렇지 않으면 1을 리턴한다. 이제 함수를 빠져 나오고 리턴된 값이 0이면 변수 foo의 값은 0이다. 만일 1이 리턴되면, 변수 foo의 값은 1이다. 이 예제에서 그 다음에 행해진 일은 변수 foo의 값을 체크해서 그에 해당하는 메시지를 프린트하고 0(성공한 경우) 또는 1(실패한 경우)의 리턴 값을 가지고 종료한다.

결론
이것으로 bash 스크립팅에 관한 소개를 마치고자 한다. 하지만 당신의 스크립팅 공부는 아직 끝나지 않았다. 더 알아야 할 것이 많다. 내가 말한 대로, 이 글은 bash 스크립팅에 관한 소개의 글이지만 당신이 쉘 프로그램을 수정하고 당신 자신의 쉘 프로그램을 작성하기 위한 디딤돌이 되기에는 충분하다. Learning the bash shell, 2nd Edition by O'Reilly & Associates, Inc을 구입하기를 강력히 추천하는 바이다. bash 스크립팅은 매일 매일의 관리자 작업을 할 때 사용하기에 매우 훌륭하다. 하지만 당신이 좀 더 큰 프로젝트를 계획하고 있다면, C나 Perl과 같은 보다 강력한 언어를 사용하기 원할 것이다. 행운을 빈다.