리눅스와 함께 꾸준히 발전해온 Perl. “CGI 최적언어”라는 평가를 받은 지는 이미 오래지만 국내에 Perl 전문개발자가 의외로 적고 Perl의 쓰임세 또한 가볍게 여기는 경향이 있는 게 현실이다. 단순히 Perl은 C와 문법이 비슷하고 CGI를 제작하기에 적당하다는 정도로 인식하고 있는 독자라면 연재될 글들을 주의 깊게 살피기를 권하고 싶다. 필자가 연재하게 될 내용은 이미 많은 책자에서 거론되었을 법한 것도 있고 전혀 색다른 시도라고 느껴지는 것도 있을 수 있을 것이다. 지면 관계상 원하는 모든 내용을 실을 수는 없겠지만 적어도 C보다는 간결하고 쉽게 구현되는 결과물을 접할 수 있도록 구성할 계획이다.
Ⅰ. Perl 입문
필자는 Linux를 접하기 전부터 Perl을 이용해 배포용 CGI(http://way.co.kr)를 위시로 하여 개발해왔으며, Linux를 접하게 된 후부터는 Linux가 Perl 프로그램 개발에 더없이 편리한 환경을 제공한다는 것을 알 수 있었다. Linux 설치를 끝냈고 C나 기타 언어를 학습중이거나 학습할 계획인 리눅서라면 Perl을 공부해보라고 권하고 싶다.
Perl의 기능은 C에서 많이 빌려왔으며 sed나 awk 등의 문자열 처리용 프로그램에서도 많은 기능을 계수하였다. 그래서 C를 어느 정도 알고 있는 사람이 Perl 소스를 접하게 되면 유사한 문법 때문에 대략적인 윤곽을 파악할 수 있게 된다. 그렇다고 Perl을 배우기 위해 일부러 C를 먼저 배우는 어리석음은 범하지 말기를 바란다.
요즘 필자는 Linux에서 vi에디터를 이용해 Perl 프로그램을 개발하고 있다. 대부분의 Linux 배포판은 Perl이 기본적으로 설치되고 레드햇 등의 배포판은 rpm과 같은 바이너리 패키지 형태로 제공되어 설치 및 관리가 간편하다. 만약 Perl을 직접 컴파일하여 설치하려거든 http://www.perl.com/등에서 파일을 다운로드하면 되지만 구체적인 설치방법은 지면관계상 생략하기로 한다. 만약 윈도우즈 계열의 서버에서 테스트해보고 싶다면 http://way.co.kr의 “CGI 강좌” 게시판을 참고하기 바란다.
먼저 Perl이 설치되어있는지 확인해보자. 대부분의 경우 /usr/bin 경로에 perl 프로그램이 위치할 것이다. 레드햇의 경우 $ rpm -qa | grep perl 명령으로도 설치여부를 확인할 수 있다. perl -v를 입력하면 설치된 Perl의 버전정보를 얻을 수 있으며 perl -V는 좀더 상세한 정보를 얻을 수 있다.
$ perl -v
This is perl, version 5.004_05 built for i386-linux (with 1 registered patch, see perl -V for more detail)
Copyright 1987-1998, Larry Wall < 하략 >
프로그래밍 언어 중에는 C 등과 같이 컴파일을 통해 독자적인 파일 하나로도 프로그램 실행이 가능한 언어가 있다. 하지만 Perl은 프로그램 소스를 해석하여 실행해주는 프로그램(인터프리터, interpreter)이 설치되어 있어야만 하고 그 때문에 Perl이나 shell 스크립트는 소스의 첫 번째 줄에 #!/usr/bin/perl 등과 같이 해석용 프로그램의 위치를 적어주어야 하는 것이다. 그러니까 Perl이 설치되어있지 않은 서버에서는 Perl 소스 프로그램을 실행할 수 없다고 보면 되는 것이다.(최근에는 소스를 컴파일 하는 방법도 제공되고 있다.) 1. 실행방법
Perl 소스 프로그램에 실행권한(chmod +x)을 부여하고 첫 번째 줄에 Perl의 위치를 적어줄 경우 다음과 같이 프로그램명만으로 직접 실행시킬 수 있다. 특히 웹상에서 실행하고자 할 경우 이와 같이 해주어야 한다.
$ program(프로그램명)
현재 경로가 PATH에 걸려있지 않다면 ./program과 같이 실행하여야 한다. 만약 shell 상태에서 다음과 같이 실행한다면 소스 첫 번째 줄에 Perl의 위치를 적어주거나 실행권한을 부여할 필요가 사라진다.
$ perl 프로그램명
작성된 소스코드의 오류를 점검하기 위해서는 perl -c 옵션을 이용한다. 소스코드에 이상이 없을 경우 “syntax OK”라는 메시지가 나타난다.
$ perl -c 프로그램명
2. 프로그램 맛보기
Perl이 시스템에 설치되어있다면 이제부터 간단한 Perl 문법부터 익혀보도록 하자.
print “Hello, Way!\n”;
간단하면서도 완벽한 하나의 프로그램이다. vi 등의 편집기에 소스내용을 입력하여 저장한 후 실행해 보자.
$ perl hello(파일명) Hello, Way!
소스를 분석해보면, “Hello, Way!”라는 문자열과 함께 줄 바꿈 문자(\n)를 출력하는 것이며 각 줄의 끝은 ;(세미콜론) 기호로 마무리한다.
3. 변수와 기본연산
다른 언어들과는 달리 변수형을 지정할 필요 없이 대입과 동시에 변수형이 결정된다. 변수는 $기호로 시작되며 배열의 경우 @, 조합배열(hash)은 %기호로 시작된다. $bom = 123; 과 같이 $bom 변수에 123이라는 정수를 대입하게 되면 별도의 조치 없이 정수형을 갖게 된다. 변수간의 연산도 매우 자유롭다. 예를 들어 다음과 같은 경우 결과값은 정상적인 수치 연산결과인 100이 출력된다.
$bom = 49; # 정수 $danvi = “51”; # 문자열 print $bom + $danvi; # 100 출력
perl 문법상 # 기호 이후부터는 주석으로 인식한다. 예상하듯이 Perl에서는 사칙연산에 +, -, *, / 기호가 이용되며 나머지(계수)에는 % 기호가 사용된다. 문자열 연산자는 .(마침표, dot)를 사용하며, 연산방법 또한 앞에서와 같이 간단하다.
$bom = 100; # 정수 $danvi = “200”; # 문자열
print $bom . $danvi; 와 같이 문자열 연산자를 이용하면 “100200”이 출력되고 print $bom + $danvi; 와 같이 수치연산자를 이용하면 300이 출력된다.
4. 조건문
if (조건) { 참일 때; } else { 거짓일 때; }
두 개 이상의 조건문은 elsif(“elseif”가 아님에 주의)를 이용하여 다음과 같이 구성할 수 있다.
if (조건) { 참일 때; } elsif (조건) { 새로운 조건이 참일 때; } elsif (조건) { 새로운 조건이 참일 때; } . . . . else { 모두 거짓일 때; }
조건판별을 위한 연산자로서 ==(eq)는 일치, !=(ne)는 불일치로 사용되며 <, >, <=, >= 등의 기호를 사용할 수 있다. 또한 논리연산자로서 &&(and), ||(or), !(not)도 사용할 수 있다.(기호와 영문 모두 사용할 수 있다.)
$bom = 5; if ($bom < 10 and $bom > 3) { print “10보다는 작고 3보다는 크다. \n”; }
소스를 실행해보면 당연히 “10보다는 작고 3보다는 크다.”가 출력될 것이다.
if와는 반대로 사용되는 조건문으로는 unless가 있다.
unless (조건) { 거짓일 때; } else { 참일 때; }
5. 반복문
while (조건) { 조건이 참인 동안 실행; } until (조건) { 조건이 거짓인 동안 실행; } 무한루프를 원치 않는다면 당연히 중괄호({})를 실행하는 동안 조건과 관련한 값이 변화되어야 할 것이다.
$bom = 1; while($bom <= 10) { print $bom, “\n”; $bom++; }
초기의 $bom의 값은 1이었고 해당 값이 10보다 작기 때문에 while문의 조건에 일치하므로 중괄호({}) 가 실행된다. 중괄호로 처리방향이 넘겨진 후 print 문에 따라 $bom의 값인 1이 출력된다. 만약 $bom++; 와 같이 $bom 값을 변화시켜주지 않는다면 $bom 값은 영원히 1일 것이므로 무한루프가 되어버릴 것이다. $bom++ 는 자동증가를 뜻하는 것으로서 원래의 $bom 값에 1을 가산한 후 다시 $bom에 값을 저장하게 되는 것이다. $bom-- 는 그 반대이다. 루프를 한번 돌게되면 $bom 값은 2가 되고 한번씩 더 돌 때마다 3, 4, 5, 6, 7, 8, 9, 10이 된다. $bom 값이 10일 경우 10을 출력하고 $bom++ 에 따라 $bom 값이 11이 되면서 while문의 조건에 맞지 않아 루프는 결국 종료된다.
또다른 반복문으로는 for문이 있다.
for (초기값; 조건; 변화값) { 조건이 참인 동안 실행; }
다음 소스는 앞서 설명한 while문의 예제와 동일한 결과를 보이게 된다.
for ($bom = 1; $bom <= 10; $bom++) { print $bom, “\n”; }
반복문중에는 foreach문도 있으나 배열과 관련하여 주로 사용되므로 설명을 뒤로 미루기로 하겠다.
6. 파일 핸들링
파일을 읽거나 저장하기 위해서는 파일을 열어야하며 일반적으로 다음과 같은 규칙에 의해 사용되어진다.
open(파일핸들, “[조작방법] 파일명”);
‘파일핸들’은 사용자의 식별을 용이하게 하기 위해 임의로 지정하여 사용한다. ‘조작방법’에는 읽기전용(< 또는 생략), 쓰기(>), 덧붙이기(>>) 등이 있다.
open(FILE, “>bom”); print FILE “Way\n”; close(FILE);
분석해보면 다음과 같다.
open(FILE, “>bom”);
bom이라는 파일명으로 파일을 쓰기모드로 열되 파일핸들은 FILE로 지정한다.
print FILE “Way\n”;
FILE이라는 파일핸들(실제로는 bom이라는 파일)에 “Way\n”라는 문자열을 기록한다.
close(FILE);
FILE 이라는 핸들의 파일을 닫는다. 조작이 끝나는 즉시 닫는 게 바람직하다.
앞에서 알 수 있듯이 파일핸들을 사용함으로 해서 특정 파일에 print(기록)하고, 손쉽게 파일을 닫을 수 있게 된다. 소스가 실행되게 되면 bom이라는 파일이 생성되어지고 파일에는 Way라는 내용이 들어가게 될 것이다. 쓰기모드일 경우 이미 동일한 파일이 존재하면 내용을 덮어쓰게 되므로 종전내용을 잃게 된다. 하지만 덧붙이기(>>)모드로 파일을 열게 되면 종전내용의 하단에 새로운 내용이 추가된다.(대부분의 log파일 저장방식과 동일하다.)
이제 다음과 같은 소스를 작성하여 실행해보자.
open(FILE, “>>bom”); print FILE “Way2\n”; close(FILE);
특별한 문제가 없는 한 bom이라는 파일내용은 다음과 같을 것이다.
$ cat bom Way Way2 $
저장하는 방법을 알았으니 이제 읽어 오는 방법을 알아보자. open(FILE, “bom”); $line1 = <FILE>; $line2 = <FILE>; close(FILE);
<FILE>이 어떤 기능을 하는지 궁금할 것이다. <FILE>에서 FILE은 앞에서와 마찬가지로 파일핸들이며 <>(‘줄입력 연산자’라고도 한다.)로 묶이므로 해서 한 줄을 읽어 온다는 뜻이다.
$line1= <FILE>; bom파일의 첫 번째 줄 내용을 $line1 이라는 변수에 저장(대입)한다.
$line2 = <FILE>; 한번 더 <FILE>을 사용하였으므로 두 번째 줄을 읽어 $line2에 저장하는 것이다. 그런데 모든 파일의 내용을 하나의 변수에 넣어 조작하고자 한다면 몇 줄인지 알 수 없기 때문에 앞의 방법보다는 다음과 같이 while문을 이용하는 것이 편리하다.
open(FILE, “bom”); while($line = <FILE>) { $all_line = $all_line . $line; } close(FILE);
약간 복잡하게 느껴질 수도 있을 것이다. 첫줄과 마지막 줄은 설명이 필요 없을 듯 하여 가운데부분만 설명하도록 하겠다. while문은 참일 경우 바로 뒤에 위치한 중괄호({})가 실행된다. Perl에서는 아무런 값이 없을 경우 ‘거짓’을 돌려주므로 bom 이라는 파일을 모두 읽어오고 나면 while 루프가 종료되는 것이다. 약간 더 설명을 덧붙이자면, $line = <FILE> 이 참일 경우(읽어올 줄이 있을 경우는 참이 된다.) $line 변수에 읽어온 줄의 내용을 저장하고 문자열연산자(.)에 의해 $all_line 변수에 추가되며, 파일 끝까지 읽고 나면 읽어올 줄이 없어 거짓을 리턴 하므로 while 루프가 종료되는 것이다.
다음과 같이 간단히 나타낼 수도 있다.
open(FILE, “bom”); while(<FILE>) { $all_line .= $_; } close(FILE);
$all_line .= $_ 부분은 $all_line = $all_line . $_와 동일한 결과를 보인다.
이제 특정파일의 내용을 읽어 변수에 저장하였으며 이로써 조작이 가능하게 되었다.
파일을 열거나 조작하고자 할 때 발생할 수 있는 오류를 미리 예방하고 파일(또는 디렉토리)에 관한 정보를 얻어 활용하기 위해 Perl은 다양한 파일테스트 연산자를 제공한다.
주요 연산자는 다음과 같다.
- e |
존재 여부 |
- r |
읽기 가능 여부 |
- w |
쓰기 가능 여부 |
- x |
실행 가능 여부 |
- d |
디렉토리 여부 |
- T |
텍스트파일 여부 |
- B |
이진파일(binary) 여부 |
- s |
파일크기 |
if (-e “bom”) { print “파일 있음\n”; } else { print “파일 없음\n”; }
print “bom 파일의 크기는 “, -s “bom”, “ byte입니다.\n”;
7. 함수
구조적인 프로그램을 위해, 특히 빈번히 사용되는 특정 기능이나 처리절차를 하나의 함수로 생성하여 활용할 수 있다.
함수는 다음과 같이 구성되며 &함수명과 같이 호출한다. 함수는 중괄호({})로 묶이며 함수영역 내외를 불문하고 사용되어지는 문법은 동일하다.
sub 함수명 { 처리내용; }
앞서 설명했던 for문을 이용해 함수를 적용해보겠다.
for ($bom = 1; $bom <= 10; $bom++) { print $bom, “\n”; &job; }
sub job { print “===============\n”; }
소스를 실행해보면 1부터 10까지 출력되는 과정중 숫자 사이사이에 구분선이 그어질 것이다. 이런 용도의 함수는 작업을 좀더 간결히 하는 정도의 효과밖에 기대할 수 없지만, 다음과 같은 경우 호출시 &함수명(“넘길값”)과 같이 인자를 넘기고 호출된 함수에서는 넘겨받은 인자를 계산(처리)하여 결과를 리턴하게되는 완전한 함수의 형태를 띄게 된다.
for ($bom = 1; $bom <= 10; $bom++) { $danvi = &job($bom); # job 함수로 $bom이라는 변수값을 넘기고, # 돌려 받은 처리결과를 $danvi 변수에 # 저장한다. print $danvi, “\n”; }
sub job { ($input) = @_; # 값을 넘겨받는다. $output = $input * 10; # 계산한다. return $output; } # 결과를 돌려준다.
소스를 실행해보면 10, 20, 30, ... 과 같이 $bom 값에 10을 곱한 값이 순서대로 출력된다.
8. 배열
여러 개의 값들을 하나의 변수에 저장하여 활용하기 위해 배열(array)과 조합배열(hash)이 지원된다. 둘의 차이점은 인덱스(index)가 숫자인가 그렇지 않은가 이다.
배열에 값들을 직접 대입하는 방법은 괄호를 이용하여 다음과 같이한다. @way = (1, 2, 3, 4, 5);
배열대입은 다음과 같이 개별적으로도 가능하다. $way[0] = 1; $way[1] = 2; $way[2] = 3; $way[3] = 4; $way[4] = 5;
배열을 일반변수형태로 추출하기 위해서는 다음과 같이 사용한다. $way[배열번호]
첫 번째 값의 배열번호는 0이다. 따라서 $way[0]에는 1이, $way[1]에는 2가 저장되어 있는 것이다.
배열에서 값을 추출하여 또 다른 변수에 저장해보자. $bom1 = $way[0]; $bom2 = $way[1]; $bom3 = $way[2]; $bom4 = $way[3]; $bom5 = $way[4];
또 다른 방법으로는 다음과 같은 방법도 존재한다. 배열에 직접 대입하던 것과는 반대이다. ($bom1, $bom2, $bom3, $bom4, $bom5) = @way;
조합배열은 %기호를 사용하며 숫자인덱스 대신 임의의 문자열(key)을 인덱스로 갖는다. 따라서 저장된 순서는 그다지 큰 의미가 없다.
%way = (“빨강” => “red”, “파랑” => “blue”, “검정” => “black”);
앞서 설명한 배열과 동일한 방법으로 대입하였지만 주의 깊게 관찰할 부분은 => 기호를 이용해서 key와 value로 구성된다는 것이다.
마찬가지로 $way{‘빨강’} = “red”; 와 같은 방식으로 개별적인 저장도 가능하다.
자료를 추출하기 위해서는 다음과 같이 사용하면 된다.
$way{key}
$way{‘빨강’} 에는 “red”가, $way{‘파랑’}에는 “blue”가 저장되어 있다.
보통 => 기호 대신 ,(comma)를 사용하기도 하지만 가독성을 높이기 위해 => 기호를 사용하는 것이 바람직하다.
print “파랑색은 영문으로 $way{‘파랑’}입니다.\n”;
소스가 실행되면 “파랑색은 영문으로 blue입니다.”가 출력될 것이다. 조합배열은 keys와 values를 이용해 인덱스나 값만을 추출해낼 수 있다. print keys %way 소스가 실행되면 “빨강파랑검정” 등과 같이 출력된다.
이제 앞에서 설명을 미뤘던 foreach문에 대해 알아보자.
foreach 추출값 (배열 또는 목록) { 실행영역; }
배열값을 하나씩 추출하고 그때마다 중괄호로 묶인 실행영역을 거치게 된다. 배열의 모든 원소가 추출되고 나면 루프는 종료된다.
@bom = (“봄”, “여름”, “가을”, “겨울”); foreach $danvi (@bom) { $count ++; print “$count, $danvi\n”; }
소스를 실행하면 다음과 같이 출력될 것이다.
1, 봄 2, 여름 3, 가을 4, 겨울
foreach문은 다음과 같이 사용할 수도 있다. 결과는 앞의 결과와 동일하다.
foreach $danvi (“봄”,”여름”,”가을”,”겨울”) { $count ++; print “$count, $danvi\n”; }
배열은 @way = (); 또는 %way = (); 와 같이 초기화할 수 있다.
배열에 원소를 추가하고자 할 경우 push를 이용한다. push(배열명, 추가할 값);
@way = (1, 2, 3, 4, 5); push(@way, 6);
@way 에는 6이라는 원소가 추가된다.
9. 문자열 처리
Perl은 문자열 처리에 상당한 능력을 과시하고 있다.
“ ”기호로 묶인 상태에서 변수를 직접 사용할 수 있다. $way = “CDE”; $way1 = “AB$way”; # ABCDE
특정 문자를 지정한 수량만큼 복제하기 위해 x 라는 특수한 연산자를 이용한다. 구분선 출력이나 열을 맞추기 위한 공백 등에 자주 사용된다. $way2 = “=” x 10; # ==========
문자열중 일부분을 추출하는 방법은 다음과 같다. 시작위치는 0부터이다.
substr(변수, 시작위치, 글자수);
$way 라는 변수의 값중 첫 번째부터 2글자를 추출하여 $way3에 저장하는 방법은 다음과 같다. $way3 = substr($way, 0, 2); # CD
문자열 중에서 특정문자의 존재여부를 파악하는 방법은 다음과 같다. 실행결과 “있음”이 출력된다.
$way = “봄 여름 가을 겨울”; if($way =~ /봄/) { print “있음\n”; } else { print “없음\n”; }
문자열을 치환(substitution)하려면 다음과 같이 하면 된다. 실행결과 “이것이 사랑일까?”가 출력된다.
$way = “이것이 우정일까?”; $way =~ s/우정/사랑/; print “$way\n”;
10. 시스템 명령
`(역따옴표)기호를 이용할 경우 원하는 시스템 명령을 실행할 수 있으며 실행결과를 얻어와 활용할 수 있다.
$way = `ls -al`; print $way;
이렇게 할 경우 shell에서와 마찬가지로 ls 명령결과가 출력된다.
$bom = `w`; @user = (“way”, “lawwal”, “danvi”); foreach $user_id (@user) { &login_check($user_id); }
sub login_check { ($id) = @_; if ($bom =~ /$id/) { print “$id : On-Line\n”; } else { print “$id : Off-Line\n”; } }
필자가 사용중인 서버에서 소스를 실행한 결과 다음과 같이 출력되었다.
way : On-Line lawwal : On-Line danvi : Off-Line
만약 결과를 얻어올 필요가 없고 명령에 따라 부수적인 작업을 해야 한다면 system이나 exec를 사용한다. exec의 경우 실행중인 Perl 프로그램은 종료되고 지정된 명령어가 실행된다는 차이점이 있다.
print “top 명령을 실행할까요? [Y/n] “; $input = <STDIN>; if ($input =~ /y/i) { system(“top”); } else { print “취소되었습니다.\n”; }
줄입력연산자인 <> 사이에 STDIN(표준입력)이 들어가게 되면, 키보드로부터 값을 입력받는다. 입력된 값에 Y 또는 y가 존재하는 경우(i 옵션은 대소문자를 구별하지 않는다.) top 명령을 실행하도록 한다.
이번에는 대략적인 문법을 위주로 설명하였다. 기본적인 문법을 토대로 하나씩 응용범위를 늘려가다 보면 여러분도 어렵지 않게 쓰임세 있는 Perl 프로그램을 개발할 수 있을 것이다.
다음에는 웹프로그램을 위한 CGI로서의 Perl을 살펴보도록 하겠다. |