Linux에서의 /etc/sudoers 수정하여 특정 사용자에게 패스워드 실행시키기

우선 이 파일에서는 mask가 잡혀 있고, /etc/sudoers에 chattr로 해서 함부로 수정이 안가게 해놓은 상태이다.

www 계정에 대해서 sudo 수행시 패스워드 입력 없이 ifdown,ifup을 사용할 수 있도록 조치하였다.

 

 

 


chattr -i /etc/sudoers
chmod u+w /etc/sudoers

 

echo "Defaults:www !authenticate" >> /etc/sudoers
echo "Cmnd_Alias IFUPDOWN=/sbin/ifup,/sbin/ifdown" >> /etc/sudoers
echo "www ALL=IFUPDOWN:ALL=/sbin/swapon" >> /etc/sudoers

 

chmod u-w /etc/sudoers
chattr +i /etc/sudoers

Posted by '김용환'
,

이 문제는 어쩌다가 일어난다. 혹시 이런 문제에 고생하는 분에게 힌트가 되었으면 한다.

 

문제

아파치의 RewriteRule을 이용하여 특정서버로 redirect를 하는데, 한쪽 서버에만 접근하는 문제가 발생했다.

A클래스 타입의 ip만 접근하고, B클래스 타입의 ip는 접근하지 않는다.

 

해결

아파치 내부에서 gethostbyname함수를 사용하도록 한다.

 

자세한 내용.
ipv6 의 스펙 문서 "Default Address Selection for IPV6"를 참조한다. ftp://ftp.rfc-editor.org/in-notes/rfc3484.txt
RFC3484 를 의거하면, DNS 에서 돌려주는(round robin) IP 가 어떤 variation 을 갖느냐에 따라서 한쪽으로 몰릴 수 있다.
다음은 RFC3484의 Rule 9이다.

 

Rule 9:  Use longest matching prefix.
   When DA and DB belong to the same address family (both are IPv6 or both are IPv4): If CommonPrefixLen(DA, Source(DA)) > CommonPrefixLen(DB, Source(DB)), then prefer DA.  Similarly, if CommonPrefixLen(DA, Source(DA)) < CommonPrefixLen(DB, Source(DB)), then prefer DB.

 

즉, 주어진 아이피에 따라  getaddrinfo메소드는  특정 ip대역만을 얻어오게 한다. 따라서 캐쉬 서버 한 대만 줄기차게 요청하는 문제가 생겼다.
(A클래스, B클래스의 ip가 존재하면, A클래스 ip만 준다.)

 

사실 사람들이 이미 고민을 하고 있다.
http://people.redhat.com/drepper/linux-rfc3484.html

내부 소스를 까보면, xor를 하고 있는 구조로 되어 있다.


glibc 의 rfc3484_sort 함수를 보면 다음과 같다.

static int
rfc3484_sort (const void *p1, const void *p2)
{
  const struct sort_result *a1 = (const struct sort_result *) p1;
  const struct sort_result *a2 = (const struct sort_result *) p2;

  ...

  /* Rule 9: Use longest matching prefix.  */
  if (a1->got_source_addr
      && a1->dest_addr->ai_family == a2->dest_addr->ai_family)
    {
      int bit1 = 0;
      int bit2 = 0;

      if (a1->dest_addr->ai_family == PF_INET)
        {
          assert (a1->source_addr.ss_family == PF_INET);
          assert (a2->source_addr.ss_family == PF_INET);

          struct sockaddr_in *in1_dst;
          struct sockaddr_in *in1_src;
          struct sockaddr_in *in2_dst;
          struct sockaddr_in *in2_src;

          in1_dst = (struct sockaddr_in *) a1->dest_addr->ai_addr;
          in1_src = (struct sockaddr_in *) &a1->source_addr;
          in2_dst = (struct sockaddr_in *) a2->dest_addr->ai_addr;
          in2_src = (struct sockaddr_in *) &a2->source_addr;

          bit1 = fls (ntohl (in1_dst->sin_addr.s_addr
                             ^ in1_src->sin_addr.s_addr));
          bit2 = fls (ntohl (in2_dst->sin_addr.s_addr
                             ^ in2_src->sin_addr.s_addr));
        }
      else if (a1->dest_addr->ai_family == PF_INET6)
         {

         ...

         }

      if (bit1 > bit2)
        return -1;
      if (bit1 < bit2)
        return 1;
    }

/* Rule 10: Otherwise, leave the order unchanged.  */
  return 0;
}

 

 

          bit1 = fls (ntohl (in1_dst->sin_addr.s_addr
                             ^ in1_src->sin_addr.s_addr));
          bit2 = fls (ntohl (in2_dst->sin_addr.s_addr
                             ^ in2_src->sin_addr.s_addr));

 

A클래스의 ip와 B클래스의 ip 소팅시, 항상 A클래스만 줄 수 있는 구조이다.

 

따라서 gethostbyname을 쓰도록 해서, 사용해야 한다. 아파치 설치시
/home/www/src/httpd-2.0.52/srclib/apr/include/arch/unix/apr_private.h 파일에서 해야할 일이 있다.
HAVE_GETNAMEINFO, HAVE_GETADDRINFO이 선언된 부분을 삭제한다.
sed  -i -e '/HAVE_GETADDRINFO/d' -e '/HAVE_GETNAMEADDR/d' srclib/apr/include/arch/unix/apr_private.h

이렇게 되면, 캐쉬서버를 여러 개를 볼 수 있어서 캐쉬서버가 문제가 없도록 할 수 있다.

 

이렇게 설치하고 나서 netstat를 하게 되면,

Posted by '김용환'
,

gethostbyname, getaddrinfo 관련 한 재미있는 소스이다.

 

 

cat gethostbyname.c


#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

int main(int argc, char **argv)
{
    struct hostent     *myent;
    struct in_addr  myen;
    long int *add;

    printf ("%s\n", argv[1]);

    myent = gethostbyname(argv[1]);
    if (myent == NULL)
    {
        printf("ERROR : ");
        exit(0);
    }

    printf("%s\n", myent->h_name);

    while(*myent->h_addr_list != NULL)
    {
        add = (long int *)*myent->h_addr_list;
        myen.s_addr = *add;
        printf("%s\n", inet_ntoa(myen));
        myent->h_addr_list++;
    }
    return 0;
}
 

 

 

cat getaddr.c

#include <stdio.h>
#include <netdb.h>
#include <netinet/in.h>

int main(int argc, char **argv)
{
    struct addrinfo *res;
    char hostname[NI_MAXHOST];
    char addrstr[100];
    void *ptr;
    struct addrinfo hints;
    int error;

  memset (&hints, 0, sizeof (hints));
  hints.ai_family = PF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags |= AI_CANONNAME;


    printf("%s\n", argv[1]);
    if (error = getaddrinfo(argv[1], NULL, &hints, &res))
    //if (error = getaddrinfo(argv[1], NULL, NULL, &res))
    {
        printf("error using getaddrinfo: %s\n", gai_strerror(error));
    }

    if (res) {
        if (error = getnameinfo(res->ai_addr, sizeof(struct sockaddr), hostname, sizeof(hostname), NULL,0,0)) {
            printf("error using getnameinfo: %s\n", gai_strerror(error));
            return 0;
        }
    }


    while (res)
    {
      inet_ntop (res->ai_family, res->ai_addr->sa_data, addrstr, 100);

      switch (res->ai_family)
        {
        case AF_INET:
          ptr = &((struct sockaddr_in *) res->ai_addr)->sin_addr;
          break;
        case AF_INET6:
          ptr = &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr;
          break;
        }
      inet_ntop (res->ai_family, ptr, addrstr, 100);
      //printf ("getaddinfo test : IPv%d address: %s (%s)\n", res->ai_family == PF_INET6 ? 6 : 4, addrstr, res->ai_canonname);
      printf("%s(%s)\n", addrstr, res->ai_canonname);
      res = res->ai_next;
    }

    freeaddrinfo(res);

}

 

 

 

 

Posted by '김용환'
,

How do I get awk to run shell scripts?

Use the system() function, e.g.:

cmd = "/bin/echo hello";
system(cmd);
close(cmd);

You can run anything you want with system(), not just shell scripts

 

 

 

출처 : http://hibernia.jakma.org/~paul/awk-faq.html#system

'c or linux' 카테고리의 다른 글

아파치의 rewriteRule 문제점  (0) 2008.02.28
gethostbyname, getaddrinfo 사용한 샘플 소스  (0) 2008.02.26
쉘 스크립트에서의 함수 파라미터  (0) 2007.09.11
ulimit  (0) 2007.09.10
crontab 에러  (0) 2007.09.08
Posted by '김용환'
,

쉘 스크립트에서의 함수

 

난 csh은 모르니까. 그냥 bash로 얘기한다.

 

선언은 다음과 같다.

function 함수이름 {

}

 

이렇게 선언하고, 다음과 같이 사용한다.

 

 

#!/bin/bash

workspace="/home/www/work"

function do_run {
    CMD=$@
    eval 'expect.pl -s "rlogin -l www a69" -c "$CMD;exit" -timeout 600'
}

TARGET=$1
if [ -z $TARGET ]; then
    echo "Usage : rsync-images.sh "
    exit;
fi

CMD="rsync -av --exclude '*/CVS/*' /data/$TARGET han::avatar/"

echo "Target Servers a69"
do_run $CMD
~

 

파라미터는 함수에서 $1 $2  이렇게 토큰단위로 쓸 수 있고, 만약 전체 스트링을 다 받고 싶다면, $@ 이렇게 써주면 전체를 함수안으로 그대로 넣어줄 수 있다.

 

 

'c or linux' 카테고리의 다른 글

gethostbyname, getaddrinfo 사용한 샘플 소스  (0) 2008.02.26
awk상에서 시스템 명령어 사용하기  (0) 2008.02.13
ulimit  (0) 2007.09.10
crontab 에러  (0) 2007.09.08
crontab 디렉토리  (0) 2007.09.08
Posted by '김용환'
,

ulimit

c or linux 2007. 9. 10. 08:16


처리할수 있는 최대 파일수
[root@sky279 14:48:35 ~]# cat /proc/sys/fs/file-max
32768

limit의 결과는 계정마다 다르게 나온다.

[root@sky279 14:50:06 ~]# limit
cputime         unlimited
filesize        unlimited
datasize        unlimited
stacksize       8192 kbytes
coredumpsize    unlimited
memoryuse       unlimited
vmemoryuse      unlimited
descriptors     16000
memorylocked    unlimited
maxproc         16000

 

실제 소스 확인
[root@sky279 14:53:40 /usr/src/linux-2.4/include/linux]# cat limits.h
#ifndef _LINUX_LIMITS_H
#define _LINUX_LIMITS_H

#define NR_OPEN         1024

#define NGROUPS_MAX       32    /* supplemental group IDs are available */
#define ARG_MAX       131072    /* # bytes of args + environ for exec() */
#define CHILD_MAX        999    /* no limit :-) */
#define OPEN_MAX         256    /* # open files a process may have */
#define LINK_MAX         127    /* # links a file may have */
#define MAX_CANON        255    /* size of the canonical input queue */
#define MAX_INPUT        255    /* size of the type-ahead buffer */
#define NAME_MAX         255    /* # chars in a file name */
#define PATH_MAX        4096    /* # chars in a path name including nul */
#define PIPE_BUF        4096    /* # bytes in atomic write to a pipe */
#define XATTR_NAME_MAX   255    /* # chars in an extended attribute name */
#define XATTR_SIZE_MAX 65536    /* size of an extended attribute value (64k) */
#define XATTR_LIST_MAX 65536    /* size of extended attribute namelist (64k) */

#define RTSIG_MAX         32

#endif

 

참조하기.

http://www.osx86.org/study/linux/procfs.txt

'c or linux' 카테고리의 다른 글

awk상에서 시스템 명령어 사용하기  (0) 2008.02.13
쉘 스크립트에서의 함수 파라미터  (0) 2007.09.11
crontab 에러  (0) 2007.09.08
crontab 디렉토리  (0) 2007.09.08
Authentication token is no longer valid  (0) 2007.09.07
Posted by '김용환'
,

crontab 에러

c or linux 2007. 9. 8. 01:31

보통 crontab -e 명령어를 통해서 작업을 하다보면, 다음과 같은 에러가 난다.

 

crontab: installing new crontab
"/tmp/crontab.XXXXScNhd2":3: bad minute
errors in crontab file, can't install.
Do you want to retry the same edit?

 

그 이유는 다음과 같다.

 

문법에 맞는 cron 스타일로 입력하지 않았기 때문에 그런 에러가 나는 것이다.

또는 dos에서 사용되는 ^M$가 라인 뒤에 붙어 있어서 문제가 될 수 도 있다..

 

 

 

 

 

 

* 참고

http://www.linuxboxadmin.com/micro/micro-how%11tos/system-administration/crontab.html

Crontab fields

Here is how the fields are defined:

  1. minute
  2. hour
  3. day of the month
  4. month of the year
  5. day of the week
  6. program or command to run

An asterisk (*) in any field means run every time for this field. Ranges (X-Y) and steps (X-Y/Z) can also be defined.

User crontabs

To edit a user crontab (including root):
crontab -e

To delete a crontab:
crontab -r

System crontab

The system crontab is stored in /etc/crontab. It can be changed (as root) with a text editor.

The system crontab has one extra field before the program to run, which is the user to run the command as (usually root).

Error installing new crontab (bad minute)

I've received this cryptic error message while trying to update a crontab:

crontab: installing new crontab
"/tmp/crontab.XXXXScNhd2":3: bad minute
errors in crontab file, can't install.
Do you want to retry the same edit?

The error message suggests a format problem with my entry, but the real error is due to DOS line endings (CR LF) in the file. Unix/Linux line endings just use CR. If a crontab is installed without using the crontab -e command, this error might appear.

You can check the line endings by running:
cat -v -e -t /var/spool/cron/username
If you see "^M$" at the end of each line, the file has DOS line endings. Each line should just end with "$".

The fix is to run the dos2unix command on the crontab to correct the line endings:
dos2unix /var/spool/cron/username

 

 

Posted by '김용환'
,

crontab 디렉토리

c or linux 2007. 9. 8. 00:54

 

crontab 지정시 tmp 폴더에 임시로 저장된다.

/var/spool/cron 의 디렉토리는 sticky 이다.

 

그러나 user별 디렉토리가 ascii이건 graphic metafile이건 상관없이 작동이 된다.

 

[i55335:/tmp]# file /var/spool/cron/www
/var/spool/cron/www: character Computer Graphics Metafile
[i55335:/tmp]# file /var/spool/cron/weblog
/var/spool/cron/weblog: ASCII text

[i55335:/tmp]# ll .
ÇÕ°è 112
drwxrwxrwt    3 root     root         4096  9¿ù  7 15:38 .
drwxr-xr-x   21 root     root         4096  7¿ù  9 15:40 ..
-rw-------    1 weblog   weblog      12288  9¿ù  6 19:09 .crontab.XXXXVMHLei.swp
-rw-------    1 root     root          102  9¿ù  7 15:34 crontab.XXXX3mdMx6
-rw-------    1 weblog   weblog        123  9¿ù  6 16:57 crontab.XXXX5fBmlX
-rw-------    1 root     root          108  9¿ù  7 15:28 crontab.XXXXCCLpty

Posted by '김용환'
,

* 에러발생

 

Sep  7 11:35:01 i55335 crond[7733]: Authentication token is no longer valid; new one required.

 

* 원인

계정관련 문제

 

* 해결

[root@i55335 ~]# chage -d 9999-12-31 e -1 weblog

 

 

*참고 자료

chage
(1) 설명: 사용자의 패스워드 만료에 대한 정보를 보여주거나 제한한다.
(2) 사용법
    chage [option] 사용자명
(3) option
   -l : 사용자계정에 대한 정보를 보여준다.
   -m : 패스워드 변경의 최소 날짜를 지정한다.
   -M : 패스워드 변경의 최대 날짜를 지정한다. 이 값이 지정하면 패스워드 만기일짜가 나타난다.
   -I : 패스워드 만료후에 실제 패스워드에 LOCK를 설정하기까지의 날짜를 설정한다.
   -E : 계정이 만기되는 날짜를 지정한다. MM/DD/YY 형태로 지정한다.
   -W : 패스워드 변경을 요구하는 경고날짜를 지정한다.
(4) 사용예
   1) [root@www root]# chage -l posein
      Minimum:        0
      Maximum:        99999
      Warning:        7
      Inactive:       -1
      Last Change:             4월 03, 2003
      Password Expires:       Never
      Password Inactive:      Never
      Account Expires:        Never
       => posein 사용자의 패스워드 및 계정 정보를 보여준다.
   2) [root@linux245 root]# chage -M 100 posein
       => 패스워드 만기를 최대 100일로 한다.
   3) [root@linux245 root]# chage -E 09/30/03 posein
       => 계정만기를 2003년 9월 30일로 지정한다.

Posted by '김용환'
,

perl에 관하여 논문 형식으로 작성한 것이다.

 

대표적인 짜집기 논문이다 -0-;; 이러면 안되는데.. 후

 

----------------------------------------------------------------------------------------

 

문자열 처리 전문가 Perl


오치환*

*동명정보대학교 컴퓨터공학과


Specialist Perl at String Handling


Chy-Hwan Oh*

*Department of Computer Engineering, Tong-Myong University, Busan , Korea


요    약 : 본 논문에서는 Perl에 관하여 논한다. 특히 Perl은 문자열을 다루는데 최고의 언어라고 할수 있다. Perl에 관하여 알아보고, 사용 되는 분야에 대해서 논할 것이다. 그 중 문자열 처리에 관한 부분에 집중하여 논한다.


핵심용어 : 문자열 처리, perl


ABSTRACT :  In this paper, We describe about Perl. Especially perl is the best language for string handling. We inquire out perl and talk about where it using. Among them, We describe about String Handling


KEY WORDS : String handling, perl

1.서론

 Perl(Practical Extraction and Reporting Language)은 임의의 형태를 갖춘 텍스트 파일을 읽고, 이 파일에서 의미있는 정보를 추출하여 이 정보를 근거한 레포트를 출력하는 제반 작업에 최적화된 언어이며, 시스템 관리에도 매우 적절한 언어이다.

또 perl은 아름다운 언어보다는 실용적인 언어를 지향한다. perl은 (이 언어의 개발자가 말하기를) C, sed, awk 그리고 sh와 같은 언어나 프로그램들의 가장 좋은점들을 취합한 언어이므로, 이 언어, 프로그램들에 익숙한 사람들이라면 perl 사용에도 역시 큰 어려움을 느끼지 않을 것이다. (컴퓨터언어 사학자라면 perl의 문법이 아마 csh이나 Pascal 혹은 BASIC-PLUS와도 닮았다고 생각할 것 같다.)

문장구조는 C 언어와 상당히 흡사하다. 대개의 Unix용 프로그램들과는 달리, Perl은 우리가 사용할 데이터 크기에 한계를 두지 않는다. 메모리만 충분하다면 한 파일의 내용 전체를 변수하나에 넣을수 있다.. 재귀(Recursion)는 무제한 반복될 수 있다. 해쉬(혹은 조합배열)에서 사용하는 테이블은 성능저하를 일으키지 않으면서 무한대로 커질 수 있다. Perl은 섬세한 패턴일치(pattern matching) 기술을 이용하여 많은 양의 데이터를 빠르게 검색할 수 있다. 비록 텍스트 검색에 최적화되어 있긴하지만 이진데이터도 다룰 수 있으며 해쉬와 같은 형태의 dbm 파일도 만들어낼 수 있다. 또 setuid를 사용하는 스크립트의 경우 C 프로그램에서 사용하는 것보다 안전한데, 데이터 흐름을 추적하는 메커니즘이 말도 않되는 대부분의 보안상의 빈틈을 막아주기 때문이다.


만일 sed나 awk, sh (혹은 bash)를 사용한 프로그램을 만들어 문제를 풀어 나갈 때, 그 문제가 이런 프로그램의 능력으로는 극복하기 어렵다거나, 혹은 프로그램이 좀 더 빨랐으면 하는데, C언어를 사용해서 어리석게도 시간낭비를 하고 싶지 않다면, Perl이 해결책을 줄 것이다. 참고로, sed나 awk 스크립트를 Perl 스크립트로 바꿔주는 변환기들을 (인터넷에서) 찾을 수 있다.

Perl은 더구나 HTML이나 사용자입력과 같은 텍스트를 다루는데 아주 강력한 기능을 가지고 있으므로 문자열 위주의 데이터를 다루기에는 최적의 언어라 할 수 있으며, UN*X, MVS, VMS, MS-DOS, Macintosh, OS/2, Amiga, Windows NT 등 여러 가지 Operating system에 Porting되어 있으므로 소스파일의 이식성이 뛰어나다.

또 Perl은 강력한 사용자층을 가지고 있다. GURU급의 사용자, 개발자로부터 초보자에 이르기까지 Perl의 확장과 사용에 많은 시간과 정렬을 제공하는 사용자들이 CPAN의 많은 모듈을 만들고, 사용하고 고쳐나가면서 Perl의 발전에 힘쓰고 있다. 이 개발자, 사용자층이 있는 이상, Perl은 계속해서 발전할 것이다.

C, C++같은 언어를 써보면 그 언어에서 문자열과 메모리의 관리가 얼마나 짜증나는 일인지 안다. Perl은 그런 복잡한 것이 없다. 즉, 언어의 관리 보다는, 언어의 유용성에 초점을 맞춘 언어이다.

Perl은 무료이다. Perl을 빛내주는 모듈들도 99.9999%가 무료이다. 그리고 많은 프로그램들이 무료로 제공되고 있다.


1993년부터 시작해서 Perl 버젼 5는 거의 완전히 새로 만들어졌고, 그로 인하여 다음과 같은 내용들이 추가되었다.

- 수많은 모듈들을 이용한 모듈화(modularity)와 재사용성(reusability) : 이는 라이브러리를 좀 더 효율적으로 사용할 수 있는 방법이다.

- C 언어의 한부분으로 사용하거나, C 언어를 이용하여 확장 가능.(embeddable and extensible)

- 다른 데이터(DBM같은)를 변수에 통합하여 변수를 조작하는 것으로 원래의 데이터를 조작할 수 있는 기능. (roll-your-own magic variables (including multiple simultaneous DBM implementations))

- 서브루틴으로 내장 함수 재선언(override)하기. 서브루틴의 동적인 선언(autoload). 프로토타이핑.

- 단순한 scalar, 배열, 해쉬이상의 복잡한 데이터 구조와 익명의 함수. (arbitrarily nested data structures and anonymous functions)

- 객체지향적 프로그래밍(object-oriented programming)

- C 코드 또는 Perl 바이트코드로의 변환(compilability into C code or Perl bytecode)

- 쓰레드 지원(support for light-weight processes (threads))

- 국제화, 지역화, 유니코드(support for internationalization, localization, and Unicode)

- 변수의 사전적 범위지정(lexical scoping) - my 연산자를 이용한 변수의 접근범위 제한.

- 향상된 정규식(regular expression enhancements)

- 향상된 디버거와 내장 편집기를 사용하여 사용자와 상호작용 하는 환경 (enhanced debugger and interactive Perl environment with integrated editor support)

- POSIX 1003.1 호환 라이브러리(POSIX 1003.1 compliant library)


2. 사용분야

perl은 한 때 HTTP, HTML이 부상하면서 CGI를 위한 언어로 각광 받았다. 하지만, CGI로서의 역할은 PHP와 같은 언어나, JSP, ASP 등의 등장으로 미미해진다.

그러나, 웹의 부상과 CGI로서의 역할이라는 다소 표면적이고 번지르르한 허명뒤에는 또 다른 역할이 있었다.(물론 아직도 꼭 CGI가 필요한 곳에서는 막강한 힘을 발휘하고 있다. Apache 서버의 mod_perl이 바로 그 중 하나일 것이다.)

그 것이 바로 "관리"를 위한 역할이다. 관리는 시스템 관리만을 위한 것이 아니다. 시스템 관리는 물론이고, 데이터 관리, 웹 관련 관리 등 다양한 관리를 포함한다.


웹 관련 관리에 관해 간단히 말해 보자면, 실제적인 사용자와의 인터페이스는 다른 언어에 맡긴다지만, 가장 쉽게 생각할 수 있는 perl의 또 다른 역할이 웹사이트의 방문기록을 분석하는 것이다. 웹의 방문기록을 counter 프로그램을 이용하여 저장한다면, 그것은 약간 비효율적이다. 그 보다는 Log 파일에 저장된 내용을 추후에 분석하는 것이 대개는 좋다. 이럴 때 perl을 사용하면 일이 매우 쉬워진다. 다른 언어로 하기는 비효율적일 것이다. 왜냐하면 문자열을 다루는 데에는 perl이 최고이기 때문이다. 또, 만일 어떤 페이지의 내용이 30분이나 1시간에 한 번씩 바뀌어야 한다면(날씨관련 사이트처럼), 혹은 하루에 1, 2번 바뀌어야 한다면, perl을 이용하는 것이 가장 좋을 것이다. 주기적으로 어떤 조건에 따라서 새로운 페이지를 생성하여 HTML로 저장한 다음 사용하는 것이, 매번 php나 jsp를 이용하여 생성하는 것보다는 훨씬 경제적이고 속도도 많이 빠를 것이다. 서버가 아닌 클라이언트의 입장이 될 수도 있다. 어떤 페이지를 주기적으로 다운로드 받아서 분석한 후 데이터로 저장해야 한다면, 최고의 문자열처리 언어인 perl을 이용하는 것이 좋을 것이다. 게다가 대상 페이지가 자주 변하는 것이라면 융통성있는 perl이 막강한 힘을 부여해 줄 것이다.


시스템 개발에서도 prototype 언어로 활용할 수도 있고(예를 들어 메일 서버를 구현하기 전에 perl로 실제로 구현해 보고, 이를 C와 같은 언어로 porting 하는 경우), 다른 언어의 code 생성에 template 기반의 tool로 사용할 수도 있는 등 그 쓰임새가 엄청나다.


3. Perl as language

본 장에서는 perl 의 기초문법에 대해 알아본다.


3.1. 펄의 데이터 타입 및 특수변수

3.1.1 Perl

1986년 Unix 프로그래머인 Larry Wall이라는 사람이 자신이 맡은 업무를 보다 쉽게 처리할려고 하는 과정에서, 자신의 일을 해결할수 있는 가장 간결하고 적합한 유틸리티를 만들게 되었다, 그래서 그는 새로운 프로그래밍 언어를 발명하게 되었고, 그후 펄이란 이름으로 빠르게 번져나갔다고 한다.


3.1.2 Date Type

펄의 데이터 타입은 스칼라(scalars), 배열(arrays), 결합형 배열(associative arrays) 이렇게 세가지가 있다.

그리고, 모든 변수명을 대소문자를 구별한다. $temp와 $Temp는 서로 다른 변수가 된다.


3.1.2.1 스칼라(Scalars)

펄은 다른 언어와는 달리 모든 수와 문자열을 이 스칼라로 표현한다.

스칼라 변수명은 '$'표로 시작한다.


    $a = 2 ; - 숫자

    $b = "test" ; - 문자

    $temp = "My name is Soo" ; - 문자열


3.1.2.2 배열(Array)

스칼라의 집합을 배열이라 하며, '@'표시로 시작한다. 다음과 같이 괄호안에 ',(쉽표)'로 각 변수는 구별된다.


    @name = ("chanho", "unicorn", "jongbum", "Hongsun") ;

    @id = (1234, "unisoo", 0987) ;


또한 배열안에 또 다른 배열이 정의 될수 있다.


    @a = (1, 2) ;

    @b = (3, 4) ;

    @c = (@a, @b) ;

   

이렇게 하면 @c에는 1, 2, 3, 4의 값을 가지게 된다.

여기서 주의할 것은 배열을 스칼라에 할당 했을 경우이다.


    @a = (1, 2) ;

    @b = @a ; <-- @b는 당연히 1, 2의 값을 가지게 된다.

    $c = @a ; <-- $c는 1, 2 의 값이 아닌 2의 값을 가지게 된다.

    $c는 array내의 값을 가지는 것이 아니라, array 내의 값들의 개수를 가진다.

만일 배열내의 임의의 값에 접근하고자 할경우는,


    @a = ("name", "school", 4, "Korea") ;

    $temp1 = $a[0] <-- name

    $temp2 = $a[3] <-- Korea 의 값을 가지게 된다.


배열은 0번째부터 값을 가지게 된다. 그러니깐 위 경우는 값이 4개이므로 $a[0]은 'name'이 되고, $a[1]은 'school'이 되고, $a[2]는 '4'가 되고, $a[3]은 'Korea'의 값을 가지게 된다.


3.1.2.3 결합형 배열(Associative Arrays)

이 결합형 배열은 위의 배열과는 조금 다른 모습을 가진다. 위의 배열은 그냥 값만 가지는것에 비해, 결합형 배열은 키(key)와 이 키에 대응하는 값을 가진다. 결합형 배열은 '%'표시로 시작되며, 다음과 같다.


    %animal = ("name", "ppoby", "number", "two", "race", "jabjong") ;


이렇게 표현되어 지며, 배열의 각 요소들에 접근하기 위해서는


    $temp = $animal{"name"} ; <-- 'ppoby'

    $temp_1 = $animal{"number"} ; <-- 'two'

    $temp_2 = $animal{"race"} ; <-- 'jabjong' 의 값이 할당되어 진다.

즉 위의 예에서, 'name은 ppoby를 number는 two를 race는 jabjong' 과 쌍을 이루게 된다. 다시 말해서 위의 배열에서 키는 'name, number, race'가 되고, 각 키의 값으로는 'ppoby, two, jabjong'이 되는 것이다.


3.1.3 특수 변수(Special Variables)

여기서는 펄에서 대단히 많이 쓰이는 변수 몇가지만 소개하기로 한다.


3.1.3.1 환경 변수(Environment Variables) - %ENV

이 변수는 환경 변수를 가지고 있는 결합 배열이다. 이 변수를 이용해서 아주 쉽게 환경 변수의 내용을 얻을 수 있다. 폼으로 부터 보내어진 데이터나, 접속한 사용자의 브라우져 정보, 클라이언트의 IP 주소, 자신의 서버가 사용하고 있는 웹 프로토콜의 버젼 등 쉽게 그 정보를 얻을수 있다.

사용 방법은


    $ENV{'원하는 환경변수'} ;

    $temp = $ENV{'REMOTE_ADDR'} ; 접속한 사람의 IP주소를 알수 있다.

자주 쓰이는 변수를 정리해 보았다.

CONTENT_LENGTH

 폼과 함께 제출된 데이터 내용의 바이트 수

CONTENT_TYPE

 폼과 함께 제출된 데이터 내용의 타입

HTTP_USER_AGENT

 웹 브라우져 소프트웨의 버젼

PATH_INFO

 요청과 함께 웹 서버로 보내어진 경로

QUERY_STRING

 폼 제출로 부터 데이터를 담고 있는 문자열

REMOTE_ADDR

 클라이언트의 IP주소

REMOTE_HOST

 클라이언트의 호스트 이름

REQUEST_METHOD

 스크립트를 호출하는 방법(GET, POST)


3.1.3.2 프로그램 인자 - @ARGV

펄 명령 라인 내에서 지정된 인자들은 특수 배열인 @ARGV를 통해 펄 스크립트 내로 전달 되어 진다.


3.1.3.3 현재 라인 - $_

특수 변수 '$_'는 입력의 현재 라인을 저장한다.


3.1.3.4 $(숫자) - $1, $2, $3 .....

특수 변수 $n는 패턴에 의해 매칭된 변수를 저장할 때 사용 되어진다. 첫번째 매칭된 문자열은 $1에, 두번째 매칭된 문자열은 $2에... n 번째 매칭된 문자열은 $n 에 기록 된다.


3.2. 제어문, 파일제어

3.2.1 연산자(Operator)

우선 Perl에서 쓰이는 대표적인 논리적 연산자에 대해서 알아보겠다. 논리적 연산자로는 ||(or) 와 &&(and)가 있다.

이 두개의 연산자는 양쪽에 오는 값에 따라 참, 또는 거짓의 값을 되돌린다.

||(or)

|| 연산자는 '또는' 이란 뜻으로, 'A || B' 일 경우 'A 또는 B 가 ...' 라는 뜻이 되며, A,B 두 값중 하나만 참이 되더라도 전체의 값은 참이 된다. 두개의 값이 모두 거짓일 경우 전체의 값이 거짓이 된다.

&&(and)

&& 연산자는 '그리고' 이란 뜻으로, 'A && B' 일 경우 'A 그리고 B 가 ...' 라는 뜻이 되며, A,B 두 값중 하나만 거짓이 되더라도 전체의 값은 거짓이 된다. 두개의 값이 모두 참일 경우 전체의 값이 참이 된다.

그리고 이 두개의 연산자는 이런 비교의 경우가 아닌, 펄 문장들을 결합시키는 역할 또한 합니다. 이때 '||'연산자는 '그렇지 않다면..' 이 되고 '&&' 연산자는 '그렇다면..' 으로 해석될수 있다.

    $value > 5 || print "값이 너무 작습니다.";

    $value < 5 && print "값이 너무 작습니다.";

   

이 두 문장 모두 같은 역할을 하는데,

위의 예는 'value의 값이 5보다 크지 않다면...' 이 되고,

아래의 예는 'value의 값이 5보다 작다면....' 이 된다.


3.2.2 제어문(Flow Control)

Perl 에서 쓰이는 대표적인 제어문은 while, foreach, if등이 있고, 그 외에 until, for, unless등이 있다.


3.2.2.1 while

while()은 ()내의 조건문이 거짓이 될때 까지 문장을 반복 수행한다.


    $n=100;

    while ($n > 0) {

        print "$n\n";

    }


3.2.2.2 foreach

foreach 문은 배열을 처리할때, 아주 유용하다. foreach 다음에 오는 배열이 널값(아무런 값도 없을때)일때 까지 문장을 수행한다.


    @name = ("Kim", "young", "soo");

    foreach $temp(@name) {

        print "$temp\n";

    }


위 문장에서 $temp 변수는 처음에는 'kim'의 값을 가지며, 두번째는 'young'의 값을, 세번째는 'soo'의 값을 가지며, 네번째에서 $temp는 널값을 가지게 되므로 루프를 종료하게 된다.

@name 배열 값의 개수가 1000개라 하더라도, 위 3줄의 예로써 모든 @name 의 값을 출력할수 있다.



3.2.2.3 if... elsif.... else

if 문은 '만약 조건문이 참이라면' 의 뜻이 된다.

if... elseif.... else 가 모두 쓰인 상태라면, if(만약 이것이 참이라면...) elsif(그렇지 않다면....) else(위의 두 개 모두 아니라면...) 이런 식으로 쓰인다.


    $temp = 5;

    if ($temp == 5) { print "temp is $temp"; }

    elsif ($temp == 3) { print "temp is $temp"; }

    else { print "Not found temp"; }


3.2.2.4 조건부 표현 (Conditional Expression)

그럼 여기서 펄에서 쓰이는 관계형 연산자에 대해서 알아보겠다.


    같 음                ==  eq

    다 름                !=  ne

    부호를 가지는 다름  <=>  cmp

    보다 큼              >   gt

    보다 크거나 같음     >=  ge

    보다 작음            <   lt

    보다 작거나 같음     <=  le


3.2.3 파일 제어

여기서는 펄에서 파일의 접근에 대한 방법을 알아 보겠다. 펄에서 파일을 열기 위해 쓰는 명령어는 open()이 있다. 물론 사용하신 다음에는 close()로 닫아야한다. 여기선 텍스트 파일에 대한 이야기를 하겠지만, dbm 파일을 이용할수 있는 dbmopen()과 dbmclose()도 있다.


3.2.3.1 open(), close()

 open() 함수는 새로운 파일 핸들을 어떤 파일에 연계시키기 위해서 사용한다.


 open(FILE, "> /etc/guest.txt");

 close(FILE);

 open(FILE, ">> /etc/file.log");

 close(FILE);


이렇게 사용 될수 있다. 위 예에서 'FILE'은 새로운 파일 핸들이고, '.../filename'은 사용 할려고 하는 파일명이다.

그리고 `` '' 안의 파일명 앞에 여러가지 기호가 쓰이는데, 이것은 파일에 접근 방법을 지정할수 있다.


    '<'   읽기를 위한 파일 열기(기본값)

    '>'   쓰기를 위한 파일 열기

    '>>'  이어쓰기를 위한 파일 열기

    '+C<' 읽기와 쓰기를 위한 파일 열기

    '+>'  읽기와 쓰기를 위한 파일 열기

    '|'   (파일 이름 앞) 파일을 명령(펄에서 파일로 전송)처럼 다룬다

    '|'   (파일 이름 뒤) 파일을 명령(파일에서 펄로 전송)처럼 다룬다


open() 함수로 파일을 열었으면, 반드시 close() 함수로 닫아야 한다. 그리고 open (파일핸들, ``파일제어기호 파일명(경로포함)''); 에서 파일제어기호 와 파일명에는 콤마등의 기호가 없다.



3.3. 정규표현, 패턴매칭

3.3.1 정규 표현(Regular expression)

정규 표현(regular expression)은 일반화된 문자열을 설명하기 위한 규칙의 집합이다.

어떤 문자열이 정규 표현의 규칙을 따르고 있다면, 그 정규 표현은 그 문자열을 매치(match)한다고 한다.

예를 들면, 정규표현 'b.'는 문자열 'observe, body, abc'와는 매치를 이루고, 'b, Bell, Bob' 와는 매치하지 않다. 이 표현 'b.'는 '소문자 b가 문자열 내에 있어야 하며, 다른 문자가 뒤에 한 개 이상 있어야 하기(단,\n는 예외)' 때문이다.

펄에서는 많은 정규 표현들이 있는데, 그것들을 정리해 보았다.


3.3.1.1 정규표현 어써션(Assertions)


    기호   매치 영역            매치      매치함    매치안

    ----------------------------------------------------------------------------        '^'    문자열의 시작부분   ^fool      foolish   tomfoolery

    '$'    문자열의 끝 부분     fool$      April    fool foolish

    '\b'   단어 영역            be\bside    be       side beside

    '\B'   비단어 영역        be\Bside   beside    be side


3.3.1.2 정규표현 아톰 퀀티파이어(Quantifiers)


    기호      매치 영역           예     매치함    매치안함

    ---------------------------------------------------------------------

    '*'     없거나 하나 이상     ab*c    ac, abc       abb

    '+'       하나 이상         ab+c     abc         ac

    '?'       없거나 하나         ab?c      ac, abc       abbc

    '{n}'     n개 만큼           ab{2}c      abbc      abbbc

    '{n, }'   적어도 n개 만큼   ab{2,}c    abbc, abbbc   abc

    '{nm}' 적어도 n개, 많아야 m개 ab{2,3}c   abbc, abbbc   abbbbc


3.3.1.4 특수문자(Special Characters)


    기호   매치 영역                예     매치함 매치안함

    ----------------------------------------------------------------------------

    '\d'   숫자                     b\dd   b4d    bab

    '\D'   비숫자                   b\Dd   bad    b4d

    '\n'   새 라인(줄바꿈)      

    '\r'   캐리지 리턴      

    '\t'   탭      

    '\f'   폼 피드      

    '\s'   공백 문자      

    '\S'   비공백 문자      

    '\w'   알파벳이나 숫자                 a\wb   a2b    a^b

    '\W' 알파벳이나 숫자를 제외한 문자  a\Wb   aa^b   aabb


3.3.2 매 칭(Matching)

위의 표현들은 펄 프로그래밍시 매우 많이, 그리고 매우 유용하게 쓰인다. 보통 매치 연산자는 두개의 '/'로 둘러싸인 정규표현으로 구성된다. 그리고 특수연산자 '=~'는 첫번째 값과 두번째 값이 매치하는지에 따라 참과 거짓으로 평가 한다.


    예) $filename =~ /dat$/ && die "Can't use. dat files. \n";

   

이 예의 뜻은 만약 $filename의 끝 문자열이 'dat'로 끝나지 않는다면 'die ~ '해라라는 뜻이 된다.


연산자 '!~'는 '=~'의 반대개념이다.


    예) $ENV{'PATH'} !~ /perl/ && warn "Not sure if perl is in your path...";

   

perl의 경로가 $ENV{'PATH'}와 매치되지 않는다면.... 의 뜻이 된다.


3.3.3 대 치(Substitution)

대치는 쉬우면서 강력한 기능을 제공한다. 대치 연산자는 '/.../.../' 를 사용한다. 첫번째와 두번째 '/'에 대치될 패턴이 오게되고, 두번째와 세번째 '/'에 대치할 패턴이 오게 된다.


    예)$name = "Kimyoungsoo";

       $name =~ s/Kim/Unicorn/;

       $name의 'Kim'을 'Unicorn'으로 대치하게 된다.


4. 문자열 처리에 관한 Perl 사용 예제

본 장에서는 perl을 이용 하여 문자열을 처리하는 방법들에 대해서 자세히 알아 본다.


4.1. 다른 프로그램을 실행시켜 출력내용 읽기

다른 프로그램을 실행시키고 그 출력 내용을 읽는 방법은 여러가지가 있다.


4.1.1. Backtick


역따옴표(`...`)는 그 안에 들어있는 내용을 shell을 통해 실행하고, 표준출력(STDOUT)으로 출력되는 내용을 반환한다. 따라서,


$content = `cat /etc/passwd`;


는 passwd 파일을 간단히 읽어들인다.

물론, 단순한 파일을 읽는데는 직접 open을 해서 읽어들이는 것이 좋다. 역따옴표를 사용하게 되면, 그 내부의 프로그램을 실행하기 위해서 별도의 프로세스가 생성되어야 하기 때문에 그만큼 시스템의 자원을 소모하게된다.


4.1.2. open 함수를 이용하는 방법


open 함수에서 파일이름분에 > 대신 | (파이프)기호를 뒤에 붙이면 해당 파일이름을 실행시키고 그 출력내용을 읽을 수 있다.


#!/usr/bin/perl

# prog.pl

print "$$\n";

warn "Exiting\n";



#!/usr/bin/perl

# opener.pl

$p = open(RUN, "./prog.pl |") || die $!;

@out = <RUN>;

close RUN;


print "opener: $p\n";

print map { "opener: $_" } @out;


opener.pl에서 prog.pl을 "./prog.pl |"와 같이 하여 열었다.(open)

그 후 일반 파일 읽기에서 하는 것처럼 <RUN>를 이용하여 출력내용을 읽는다. 참고로, open 함수가 반환하는 것은, 프로그램이 제대로 실행되었을 때, 새로운 프로세스의 프로세스 아이디이다.

만일 이 프로그램이 오랜시간동안 살아 있는 것(웹서버처럼)이라면 close를 하지 않을 경우 문제가 될 수 있다. 예를 들어, open은 계속해야 하는데, close를 해 주지 않는다면, 해당 프로세스가 죽지않고 계속 남아서 시스템자원을 차지하고 있어서 결국 시스템이 멈추는 상황이 발생할 수도 있게된다.

따라서 반드시 close를 해 주어야 합니다.

만일 표준에러(STDERR)까지 같이 읽어야 한다면


#!/usr/bin/perl

# prog.pl

print "$$\n";

warn "Exiting\n";



#!/usr/bin/perl

# opener.pl

$p = open(RUN, "./prog.pl 2>&1 |") || die $!;

@out = <RUN>;

close RUN; #


print "opener: $p\n";

print map { "opener: $_" } @out;


와 같이 2>&1를 추가합니다.


4.2. 문자열 안에 실행문 넣기

문자열 내에 실행문을 직접 삽입할 수 있다.

보통의 print 문에서는

print "수익대 비용 차액 : " . ($income - $outcome) . "\n";


와 같은 식이다.

만일 print문 안에 이런 계산식이 많이 들어가야 한다면, 조금 귀찮을 수도 있다. 이런 경우 문자열 내에 계산식을 직접 삽입하는 것이 간편하겠다고 생각되면 다음과 같은 형식을 사용한다.

print "수익대 비용 차액 : ${ \($income - $outcome) }\n";


문자열 내에서 ${스칼라변수리퍼런스}는 그 리퍼런스가 가리키는 변수의 값을 나타낸다. 예를 들면 다음과 같은 경우이다.

$var = 365;

$rvar = \$var;

print "var = ${$rvar}\n"; # 이 경우라면 "var = $$rvar\n"도 같은 결과이지만, 설명을 위해 위 ${ \($income - $outcome) }는 ${ }의 안에 \($income - $outcome)을 가지고 있다. 목록(list)을 나타내는 괄호앞에 \(백슬래쉬)를 두면 목록의 각 요소에 대해 리퍼런스를 만드는 것과 같다. 그래서

$what = \( $a, $b, $c )

$what = ( \$a, \$b, \$c );

와 같게 되고, 스칼라 문맥에서 목록이 사용되면 맨 뒤의 것만 사용되게 되어 맨 마지막 요소인 $income - $outcome의 결과 값에 대한 리퍼런스가 ${ }와 함께 사용되어 계산값이 출력 되는 것이다. 맨 마지막 값이 사용되는 것을 직접 확인하려면 괄호의 앞부분에 다른 값을 넣고 컴마로 구분해 보면 된다.

print "수익대 비용 비율과 차액 : ${ \($outcome / $income, $income - $outcome) }\n";


이 경우 $outcome / $income 부분은 사라져 알 수 없다.

배열 역시 문자열 안에 넣을 수 있다. 문자열 내에서 배열리퍼런스의 사용은 @{배열의리퍼런스}의 형식이다. 물론 "@$array_ref"와 같은 맥락이다.

print "수익대 비용 비율과 차액 : @{ [ $outcome / $income, $income - $outcome ] }\n";


이 경우에는 \() 대신 []를 사용한다. []는 이름없는 배열(anonymous array)의 레퍼런스를 나타낸다.


4.3.  첫 글자는 대문자로, 나머지는 소문자로 바꾸기

영문 문자열의 첫글자만 대문자로 만들고, 나머지는 소문자로 만드는 법이다. 사용자의 입력을 받았을 때, 다양한 사용자의 입력 습관을 무시하고, 적절히 바꾸는 방법중 하나라고 생각된다.

쌍따옴표를 사용할 때, \n이 개행문자(new line)로, \t가 탭문자로 사용된다는 것을 알 것이다. 이 외에도 다양한 특수문자들이 있다. 그 중에는

\U : 문자열의 끝 또는 \E가 나올 때 까지 모든 문자를 대문자로 바꿈

\L : 문자열의 끝 또는 \E가 나올 때 까지 모든 문자를 소문자로 바꿈

\u : 바로 다음의 문자만을 대문자로 바꿈

\l : 바로 다음의 문자만을 소문자로 바꿈

과 같은 특수문자들이 있다. 이 특수문자들을 이용하면, 원하는 바를 이룰 수 있다.

$str = "what A wAnderful Day!";

$badstr  = "\u$str";   # What A wAnderful Day!

$goodstr = "\u\L$str"; # What a wanderful day!


$badstr의 경우는 굳이 설명이 필요없지만, 자칫하면 위와 같이 생각할 수 있다. (사실 프로그래머는 사용자의 입력을 ``예단''하는 수가 상당히 많다)

$goodstr은 일단 \L로 모든 문자들을 소문자로 바꾼후에 \u로 첫 문자만 대문자로 바꿔주었다.


4.4. 문자열의 맨 처음과 맨 뒤의 공백 없애기

파일에서 텍스트를 읽어 들이거나 사용자의 입력을 받을 때 때로 반드시 처리해 주어야 하는 것이 문자열의 맨 앞이나 맨 뒤의 공백문자들을 지우는 것이다. 다음의

$str =~ s/^\s+|\s+$//g;


한 줄이면 맨 앞의 공백과 맨 뒤의 공백을 한 번에 없앨 수 있다. 물론 습관에 따라

$str =~ s/^\s+//;

$str =~ s/\s+$//;


해도 무관하다.


4.5. 한글을 깨끗하게 자르기

어떤 데이터를 받거나 얻은 후, 데이터베이스에 넣거나 웹상에 표출할 때, 그 것이 너무 길어서 데이터베이스에서 받아들이지 않거나, 웹 페이지의 모양이 이상한 경우가 있다.

이 때는 받은 데이터의 일부를 불가피하게 잘라내야 하는 데, 영문이라면 큰 문제가 없지만 한글의 경우에는 문제가 된다.

이유는, 한글 한 글자를 나타내는 데 2바이트를 사용하기 때문이다.

예를 들어 "우리나라 만세"를 10 바이트로 잘라내려면 "우리나라" 8바이트, 공백 1바이트, '만'자의 앞부분 1바이트가 남아서 출력될 때는 깨진 글자가 보여지게 된다.

이 때는 다음과 같은 코드를 사용하시면 된다.


if( length($text) > 60 ) {

      $text = substr($text,0,60);

      $text =~ s/(([\x80-\xff].)*)[\x80-\xff]?$/$1/;

}


일단 문자열을 원하는 길이만큼만 남기고 잘라낸다(substr).

남은 문자열의 끝부분에 대해서 정규식을 적용하는데, 한글의 첫 바이트는 항상 \x7f 보다 크므로 [\x80-\xff]., 즉 한글의 첫바이트일 수 있는 [\x80-\xff] 한 바이트와 '.' 한 바이트가 합쳐진 것을 '*' 하면 한글 0 또는 그 이상의 글자가 된다. 그리고 한글의 첫바이트인 [\x80-\xff] 하나만 남는다면 온전한 한글 문자열부분인 (([\x80-\xff].)*) 부분이 $1 이라는 변수에 남아있으므로 그것을 일치된 전체 텍스트에 대해서 치환한다.


4.6. 날짜 패턴검사와 후참조(Backreference)

날짜 입력값의 패턴 검사를 해 보겠다.

날짜 입력값이 yyyy-mm-dd나 yyyy/mm/dd는 인정하고 그 외의 패턴은 인정하지 않는다고 한다면 다음과 같이 간단한 정규식을 사용하면 된다.


if( $dateinput =~ m|^\d{4}([-/])\d{1,2}\1\d{1,2}$| ) {

      print "Good\n";

} else {

      print "Not good\n";

}


여기서 상세한 날짜 범위에 대한 검증은 생략하겠다.

얼핏생각하면 패턴 검사를


$dateinput =~ m|^\d{4}([-/])\d{1,2}[-/]\d{1,2}$|


와 같이 하면 될 것이라고 생각할 수 있다. 하지만, 이 경우는 yyyy-mm/dd나 yyyy/mm-dd도 옳은 것으로 인정해 주기 때문에, 썩 좋은 해법이라고 보기 어렵다. 그래서


$dateinput =~ m|^\d{4}-\d{1,2}-\d{1,2}$| or $dateinput =~ m|^\d{4}/\d{1,2}/\d{1,2}$|


라고 할 수도 있으나, 그 보다는 맨 위의


$dateinput =~ m|^\d{4}([-/])\d{1,2}\1\d{1,2}$|


가 훨씬 간결하다. \1은 후참조(backreference)라고 한다. 정규식 패턴 내에서 앞의 괄호가 일치할 때, 패턴 내부에서 다시 참조할 수 있도록 별도로 저장해둔 내용을 참조하는 것이다.

이 것은 패턴 외부에서 $1, $2, $3...를 사용하는 것과는 달리, 패턴 내부에서 사용한다. 그래서 년과 월의 사이에 사용되는 구분자가 똑같이 월과 일의 사이에서 사용된 경우에만 올바른 패턴으로 결정할 수 있게 된다.

후참조 역시 $1, $2, $3...와 같이 \1, \2, \3...로 사용된다. 만일 날짜 + 시간 패턴을 검사한다면, 다음과 같이 사용할 수 있다.


m|^\d{4}([-/])\d{1,2}\1\d{1,2} \d\d([-:])\d\d\2\d\d$|


5. 결  론

Perl 을 사용 하는 많은 사람들의 인식 속에는 perl 은 그저 인터넷 분야에서 널리 활용되고 있다. 그래서 “오직 CGI 만을 위한 언어다”라고 생각 할지도 모른다. 펄을 창시한 래리 월이 TMTOWTDI, 즉 “There is more than one way to do it" 이라는 슬로건을 내 걸었듯이, 인터넷 프로그래밍은 물론이고 유닉스 시스템 관리, 네트워크, 기타 컴퓨터 분야의 전반에 걸쳐 활용될 수 있는 Perl 의 풍부한 활용성을 알리고자 이렇게 이글을 쓰게 되었다. 특히 문자열 처리에 관한한 최고임을 자부하는 perl 을 사용자 입맛에 맞게 사용 하였으면 한다.


참고문헌


[1] 톰 크리스잔센, 네이던 토킹턴 저, 안계용 역, “Perl Cookbook" 2000.

[2] "http://www.perlmania.or.kr" 문서 모음 게시판

[3] “http://www.perl.or.kr” About perl, Tips 게시판

[4] http://www.softbrain.co.kr/perl/” Perl 이야기 게시판

[5] http://www.activestate.com/” 윈도우용 perl 자료

[6] http://perl.sshel.com/” Docs 게시판

[7] http://seoul.pm.org/

Posted by '김용환'
,