Home Map Index Search News Archives Links About LF
[Top bar]
[Bottom bar]
[Photo of the Author]
글쓴이 Guido Socher

글쓴이 소개:

Guido는 오랫동안 리눅스 팬이었고 Perl 핵커였다. 요즘 그는 집을 새롭게 단장하고 정원에 샐러드와 다른 것들을 심어 키우는데 무척 바쁘다.

차례:

Perl 두번째

[Illustration]

요약:

Perl 첫번째에서는 펄의 일반적인 내용을 살펴보았다. perl part II에서는 첫번째 실제적인 프로그램을 작성한다.



당신의 프로그램 구조

펄은 특화된 한 태스크의 작은 프로그램을 작성하는데 최선의 선택이다. 대부분의 프로그램들에서 당신이 하려고 하는 기능이나 몇몇의 간단한 구조를 제공하는 틀을 손으로 그려보는 일은 개발 프로세스의 속도향상을 위해서 매우 좋은 생각이다. 다음의 code template은 간단한 옵션 명령어와 도움말을 프린트하는 서브루틴을 제공한다.
 
!/usr/bin/perl -w
# vim: set sw=8 ts=8 si et:
#
# uncomment strict to make the perl compiler very
# strict about declarations:
#use strict;
# global variables:
use vars qw($opt_h);
use Getopt::Std;
#
&getopts("h")||die "ERROR: No such option. -h for help\n";
&help if ($opt_h);
#
#>>your code<<
#
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
sub help{
print "help message\n";
exit;
}
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
__END__

코드를 보면 &getopts()는 명령어 옵션을 읽기 위해 라이브러리 Getopt::Std 에 있는 서브루틴을 호출한다. 이 함수는 명령어 옵션에 의해 제공된 전역 변수 $opt_<option>를 설정한다. 명령어 상의 모든 옵션은 "-" (minus sign)으로 시작하고 프로그램 이름과 다른 변수 사이에 온다. (Note: 이것은 일반적인 Unix 규칙이다.) &getopts에 주어진 문자열(위의 프로그램에서는 "h")은 프로그램이 허용하는 모든 옵션을 나열하고, 만약 옵션이 콜론으로 된 변수를 가지고 있다면 옵션 문자는 반드시 콜론뒤에 있어야 한다. &getsopt("d:x:h")는 프로그램이 -d, -x, -h라는 옵션을 가지고 있다는 의미이다. 옵션 -d와 -x는 아규먼트를 가지고 있기 떄문에 "-o somgthing"은 맞는 것이지만 "-o -x foo"는 -o 뒤에 아규먼트가 없기 때문에 틀린것이다.
만약 옵션 -h가 명령어에서 주어진다면 $opt_h변수는 &help로 정해진다. 그리고 ($opt_h);는 help로 정해지므로 옵션 -h가 명령어에서 주어지면 서브루틴 help가 불려진다고 할 수 있다. sub help{문장은 서브루틴을 선언하는 것이다. 지금 이순간 이 모든 코드의 자세한 부분을 이해하는 것을 중요치 않다. 단지 필요할때 당신의 주요 기능에 이 template을 사용할 수 있으면 된다.

template 사용하기

이 구조를 사용하여 작은 수 변환기를 만들어 보자. 16진수를 10진수로 변환시키는 이 프로그램의 이름을 numconv라고 한다.
numconv -x 30 10진수 30과 같은 16진수를 프린트 한다.
numconv -d 1A 16진수 1A와 같은 10진수를 프린트 한다.
numconv -h 도움말을 프린트 한다.
펄 함수 hex()는 16진수를 10진수로 바꾸고 printf()는 10진수를 16진수로 바꾸는데 사용할 수 있다. 이 함수들을 우리의 template집어 넣으면 훌륭한 프로그램이 될 것 이다.
 
#!/usr/bin/perl -w
# vim: set sw=8 ts=8 si et:
#
# uncomment strict to make the perl compiler very
# strict about declarations:
#use strict;
# global variables:
use vars qw($opt_d $opt_x $opt_h);
use Getopt::Std;
#
&getopts("d:x:h")||die "ERROR: No such option. -h for help\n";
&help if ($opt_h);
if ($opt_d && $opt_x){
    die "ERROR: options -x and -d are mutual exclusive.\n";
}
if ($opt_d){
    printf("decimal: %d\n",hex($opt_d));
}elsif ($opt_x){
    printf("hex: %X\n",$opt_x);
}else{
    # wrong usage -d or -x must be given:
    &help;
}
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
sub help{
    print "convert a number to hex or dec.
USAGE: numconv [-h] -d hexnum
    umconv [-h] -x decnum

OPTIONS: -h this help
EXAMPLE: numconv -d 1af
\n";
    exit;
}
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
__END__

위의 numconv프로그램을 다운로드 받으려면 클릭하시오.
다음 장에서는 우리는 이 프로그램에 대해 더욱 친숙해질것이다. 이해해 두기를 바란다.

If-문장

팔에는 2가지 형식의 if 문장이 있다.:
expr if (cond);
or
if (cond) BLOCK [[elsif (cond) BLOCK ...] else BLOCK]

BLOCK은 {}안에 있는 문장의 수를 의미한다. 이것은 If문장이 다음과 같이 쓰일 수 있다는 것을 의미한다.:
 

printf("hello\n") if ($i);

if ($i == 2){
   printf("i is 2\n");
}elsif ($i == 4){
   printf("i is 4\n");
}else{
   printf("i is neither 2 nor 4\n");
}

펄에서도 C에서 쓰는 것처럼 && 와 || 연산자를 쓸 수 있다.
printf("hello\n") if ($i);
위와 같은 코드는 아래와 같이 쓰일 수 있다.
($i) && printf("hello\n");
특별히 ||는 template안에서 끝이 난 문장안에 쓰 일 수 있다.
&getopts("d:x:h")||die "ERROR\n";
"옵션이 있으면 가져오고 없으면 끝난다.". die() 함수는 기본적으로 프로그램을 끝내고 프린트 문을 수행하는 것과 같은 기능을 한다. 이 함수는 메시지를 프린트하고 프로그램을 끝내게 된다.
&getopts("d:x:h")||die "ERROR\n";
이것은 다음과 같은 문장이다.
die "ERROR\n"; if (! &getopts("d:x:h"));
"!"은 논리적으로 부정 연산자이다. 다시 이것은 다음과 같이 쓰일 수 있다.
die "ERROR\n"; unless (&getopts("d:x:h"));
unless는 if-not과 같은 것이고 if(!..) 보다 읽기가 편하다.

당신이 본것과 같이 펄에서는 if-문장을 쓰는데 한가지 방법이 있는것이 아니다. 이 모든 방법을 다 쓸 필요는 없고, 당신이 쓰기에 편한 방법을 쓰면 된다.

변수

첫번째 글 에서 우리는 선언 안하고 쓰이는 scalar 변수($-변수)를 봤었다. 이 변수들은 사용되는 순간에 존재하게 된다. 이것은 작은 프로그램에서는 아주 좋은 기능이지만 큰 프로그램에서는 찾기 힘든 에러를 유발 시킬 수 있다. 변수의 선언은 컴파일러에게 타입 에러를 첵크하게하여 이런 가능성을 줄 일 수 있다.
"use strict;"는 모든 변수가 선언되도록 한다. 
다음과 같은 정확한 코드를 예를 보이면:
#!/usr/bin/perl
use strict;
my $i=1;
print "i is $i\n";

이 프로그램은 정확하고 i는 1이라는 결과를 출력한다. 이제 실수에 의해 i 대신에 j를 썼다고 생각하면:

#!/usr/bin/perl
#
$i=1;
print "i is $j\n";

이 코드는 펄에서 잘 수행될것이고 "i is "라는 결과를 출력한다. 펄 모듈 "use strict;"는 컴파일러에게 이러한 프로그램이 잘못됬다는 것을 알린다. "strict"를 쓸면 모든 변수는 선언되야 되고 위의 프로그램은 에러 메시지가 출력된다.
 

#!/usr/bin/perl
use strict;
my $i=1;
print "i is $j\n";

이 프로그램은 다음과 같은 메시지를 출력하고 에러 출력을 쉽게 한다.

Global symbol "$j" requires explicit package name at ./vardec line 4.
Execution of ./vardec aborted due to compilation errors.
Exit 255
펄에서 변수는 "my"를 사용하여 선언되거나 framework에서 보인것과 같이 "use vars qw()"를 사용하여 선언된다.:
use vars qw($opt_h);

전역 변수use vars에 의해 선언된다. 이러한 변수들은 라이브러리를 포함하는 모든 코드에서 전역적이다.
현재 프로그램 화일안에서 지역적인 변수(화일안의 모든 서브루틴안에서는 전역적이다.)는 서브루틴 바깥의 프로그램의 시작부분에 my 를 사용하여 선언된다.
현재 서브루틴 안에서 지역적인 변수는 서브루틴 안에서 my를 사용하여 선언된다.

사람들은 쉘 프로그래밍에서 변수를 선언하거나 값을 할당할 때 $ 표시를 무시 하는 경향이 있다. 이것은 펄에서는 불가능하며 스칼라 변수를 사용할때는 항상 $ 표시를 써야한다.

그리고 변수를 선언할때 값을 직접 할당할때도 쓰일 수 있다. my $myvar=10; 는 $myvar를 선언하고 이 변수에 초기값으로 10을 할당 한다는 의미이다.

서부루틴

우리는 위의 numconv 프로그램에서 "help" 서브루틴을 사용하는 것을 이미 배웠다. 서브루틴은 당신이 원하는 기능을 프로그램하는데 사용될 수 있기 때문에 이러한 서브루틴의 기능은 프로그램 구조에 도움이 될 것이다.
서브루틴은 프로그램안에서 어느 위치에든 삽입될 수 있다.(서브루틴이 호출되기 전이든 호출된 뒤에 쓰여지든 상관이 없다.) 서브루틴은 sub name(){... 로 시작되고 서브루틴을 호출하기 위해서는 $retval=&name(...arguments...) 와 같은 형식으로 호출해야 한다. 서브루틴 안에서 마지막으로 실행된 문장의 값은 리턴 값이다. 서브루틴에 주어진 매개변수들은 튿별한 배열인 @_를 통해 서브루틴안의 코드에게 전달된다. 배열에 대해 더 자세한 설명은 다음번 연재에 개제될 예정이고, 잠시, 서브루틴 안에서 shift를 사용해 스칼라 변수의 값을 읽는 방법을 알아보자. 여기에 예제 가 있다:
 
#!/usr/bin/perl
use strict;
my $result;
my $b;
my $a;
$result=&add_and_duplicate(2,3);
print "2*(2+3) is $result\n";

$b=5;$a=10;
$result=&add_and_duplicate($a,$b);
print "2*($a+$b) is $result\n";

# add two numbers and multiply with 2:
sub add_and_duplicate(){
    my $locala=shift;
    my $localb=shift;
    ($localb+$locala)*2;
}

실제 프로그램

많은 펄 문법과 펄 언어의 많은 구성 요소를 배운 지금, 이제는 실제 프로그램을 작성할 시간이다.
펄은 작은 프로그래밍 노력으로 텍스트 화일을 다루기 위해 디자인 되었다. 우리의 첫번째 펄 프로그램은 약어 리스트를 비교해서 리스트 안에서 중복된 것을 찾는 프로그램이다. 중복이라는 것은 리스트안에서 여러번 발견되는 약어를 이야기 하며 그 리스트는 다음과 같이 표현된다.:
펄을 사용해서 텍스트 파일을 다루는 것은 매우 쉽다.  
AC Access Class
AC Air Conditioning
AFC Automatic Frequency Control
AFS Andrew File System
...
그 리스트 화일은 이곳에서 다운로드 할 수 있다. 이 파일의 문법은 다음과 같다.: 이 텍스트 파일을 어떻게 읽을 것인가? 여기에 줄단위로 텍스트를 읽어들이는 코드가 있다.:
 

....
open(FD,"abb.txt")||die "ERROR: can not read file abb.txt\n";
while(){
   #do something
}
close FD;
....

open 함수는 파일 지시자를 첫번째 전달인자로 갖고 파일 이름을 두번째 전달인자로 가진다. 파일 지시자는 특별한 변수이다. 이것을 open함수에 쓰면 함수 안에서 파일안에서 자료를 읽을 수 있고, 마지막으로 close 함수에 전달한다. <FD>로 파일을 읽는 것이 끝나면 <FD>는 while loop에 인자로 주어질 수 있고 이경우 줄 단위로 파일을 읽는데 사용되게 된다.
전통적으로 파일 지시자는 펄에서 대문자로 표시된다.

읽혀진 자료는 어디에 있을까? 펄은 몇가지의 묵시적인 변수를 가지고 있다. 이런 변수들은 선언 없이 쓰이는데 항상 존재한다. 그중 한가지로 $_ 변수가 있다. 이 변수는 위의 while loop에서 현재 읽어들인 한 라인의 정보를 가지고 있다. 한번 다음의 코드를 실행 해보기를:(코드 받기 ):
 

#!/usr/bin/perl
use strict;
my $i=0;
open(FD,"abb.txt")||die "ERROR: can not read file abb.txt\n";
while(<FD>){
   # increment the line counter. You probably
   # know the ++ from C:
   $i++;
   print "Line $i is $_";
}
close FD;
묵시적인 변수 $_는 한 라인을 가지고 있다.

보이는 것처럼 위의 코드는 print "Line $i is $_ \n"와 같이 쓰이지 않았는데, 이것은 $_ 변수가 텍스트 화일 안에서 개행 문자(\n)를 같이 읽어 들였기 때문이다.

이제 우리는 파일을 읽는 방법을 알았다. 확실하게 우리의 프로그램을 끝내기 위해서는 우리는 두가지 사항을 더 배워야 한다.

  1. 어떻게 한 라인의 시작에서 약어를 읽어들일 것인가?
  2. 펄에서 해쉬 테이블은 어떻게 작동하는가?
정규 표현 텍스트 문자열안에서 특정 패턴을 찾기위한 정교한 의미를 제공합니다. 우리는 한 라인안에서 첫번째 공백 전까지 첫번째 문자열을 찾아야 합니다. 다른 말로 표현하면 우리의 패턴은 "라인의 시작 --> 공백없는 문자들 --> 공백" 입니다. 펄의 정규표현에서는 이것을 ^\S+\s와 같이 표현 합니다. 이것을 m//안에 집어 넣으면 펄은 이 표현을 $_ 변수에 집어넣습니다. (기억: 이 변수는 현재 라인을 가지고 있습니다. 그렇죠??). \S+는 정규표현식에서 "공백이 없는 문자들" 이라는 것과 같은 의미입니다. 만약 으로기 \S+에 가로 ()를 둘러 싸면 공백이 아닌 문자들을 변수 $1에 집어 넣게 됩니다 이것을 우리의 프로그램 에 집어넣으면 아래와 같습니다.:
#!/usr/bin/perl -w
# vim: set sw=8 ts=8 si et:

use strict;
# global variables:
use vars qw($opt_h);
my $i=0; 
use Getopt::Std;
#
&getopts("h")||die "ERROR: No such option. -h for help.n";
&help if ($opt_h);
#
open(FD,"abb.txt")||die "ERROR: can not read file abb.txt\n"; 
while(<FD>){ 
    $i++; 
    if (m/^(\S+)\s/){
        # $1 holds now the first word (\S+)
        print "$1 is the abbreviation on line $i\n"; 
    }else{
        print "Line $i does not start with an abbreviation\n";
    }

close FD; 
#
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
sub help{
     print "help text\n";
     exit;
}
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
__END__ 

만약 정규 표현이 현재 라인에 성공적으로 적용이 되면, 일치 연산자 (m/ /)는 1을 반환합니다. 그러므로, if-문장안에 이 것을 사용할 수 있습니다. $1 변수는 실제로 확실한 자료가 있는지 확인되야 하기 때문에 $1 변수를를 사용하기 전에 는 꼭 일치 연산자에 if-문장을 사용하여야 한다.

해쉬 테이블

이제 우리는 화일을 읽고, 약어를 얻어온다. 그리고 읽혀지지 않은 나머지 것들은 읽혀진 약어가 그전에 쓰였었는지 알아오기 위해 필요한 정보들이다. 이제 우리는 새로운 펄 데이터 타입이 필요하다: Hash Tables. 해쉬 테이블은 문자열에 의해 인덱스 될수 있는 배열이다. 변수 이름 앞에 % 표시를 붙여서 해쉬 테이블이라는 것을 나타낸다. 각각의 값을 읽기 위해서 $variable_name{"index_string"}와 같은 형식을 사용해야 한다. 다른 스칼라 변수와 같이 해쉬 테이블안의 필드는 일반적인 스칼라 변수와 같이 사용된다. 예제:
 
#!/usr/bin/perl -w
my %htab;
my $index;
# load the hash with data:
$htab{"something"}="value of something";
$htab{"somethingelse"}=42;
# get the data back:
$index="something";
print "%htab at index \"$index\" is $htab{$index}\n";
$index="somethingelse";
print "%htab at index \"$index\" is $htab{$index}\n";

이 프로그램이 실행될때 우리가 얻을 수 있는 것은:

%htab at index &quotsomething"은 "value of something"이고
%htab at index &quotsomethingelse" 은 42라는 것이다.
이제 우리의 프로그램은 끝이 난다:
 1  #!/usr/bin/perl -w
 2  # vim: set sw=4 ts=4 si et:
 3  # 
 4  use strict;
 5  # global variables:
 6  use vars qw($opt_h);
 7  my %htab;
 8  use Getopt::Std;
 9  #
10  &getopts("h")||die "ERROR: No such option. -h for help.n";
11  &help if ($opt_h);
12  #
13  open(FD,"abb.txt")||die "ERROR: can not read file abb.txt\n"; 
14  print "Abbreviations with several meanings in file abb.txt:\n";
15  while(<FD>){ 
16      if (m/^(\S+)\s/){
17          # we use the first word as index to the hash:
18          if ($htab{$1}){
19              # again this abbrev:
20              if ($htab{$1} eq "_repeated_"){
21                  print; # same as print "$_";
22              }else{
23                  # this is the first duplicate we print first
24                  # occurance of this abbreviation:
25                  print $htab{$1};
26                  # print the abbreviation line that we are currently reading:
27                  print;
28                  # mark as repeated (= appears at least twice)
29                  $htab{$1}="_repeated_";
30              }
31          }else{
32              # the first time we load the whole line:
33              $htab{$1}=$_;
34          }
35      }
36  } 
37  close FD; 
38  #
39  #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
40  sub help{
41          print "finddup -- Find abbreviations with several meanins in the
42  file abb.txt. The lines in this file must have the format:
43  abrev meaning
44  \n";
45          exit;
46  }
47  #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
48  __END__ 
어려분은 이 곳에서 이 프로그램을 다운 받을 수 있다.

이 프로그램은 어떻게 작동하는가? 이 프로그램은 라인 단위로 파일을 읽고, 그 라인을 %htab (line 33)라는 해쉬 테이블에 저장한다. How does it work? We read the file line by line and store the lines in our hash called %htab (line 33). 해쉬 테이블의 인덱스는 약어이다. 해쉬 테이블을 사용하기 전에 그 안에 다른 값이 저장되어 있는지 확인한다. (line 18). 만약 해쉬 테이블안에 다른 값이 있으면 두 가지 가능성이 있다.

  1. 이것은 첫번째 중복이다.
  2. 이 약어는 이미 여러번 중복되어 있다.
이 두가지 경우를 구별하기 위해 "_repeated_"라는 문자열을 해쉬 테이블안에 써서 이미 중복되었다고 발견된 것들을 표시한다.(line 29)

이 코드는 다음에서 다운 받을 수 있고 한번 실행해 보기를 바란다.

다음 연재에서는...

이번 연재에서 당신은 이미 펄 언어의 몇가지 자세한 부분들을 배웠다. 아직 펄이 가진 모든 데이터 타입을 다루지는 않았으며, 당신이 펄에서 "abb.txt" 와 같은 정해진 화일 이름을 사용하는 것을 피할 수 있는지 궁금해 할 수 있지만, 당신은 이미 화일 이름을 코드안에 확정시키않고 옵션을 사용해 가변적으로 화일 이름을 지정하는 방법을 알고있다. (예: finddup -f abb.txt) 위의 예제들을 바꿔서 코딩해보기를 바란다. 명령어 라인과 데이터타입 배열을 읽는 일반적인 방법은 다음 연재에 소개될 것이다.  
본 웹 페이지는 리눅스포커스 에디터 팀에 의해 관리 됩니다.
© Guido Socher 
LinuxFocus 1999
번역정보:
원문 : 영어
en -> kr : 이창우

1999-11-01, generated by lfparser version 0.8