오래전 이야기/Server

PERL의 이해

리눅스 엔지니어였던 2008. 9. 15. 18:49

 

펄(Perl) : 언어는 세상을 닮는다.

펄은 sed, grep, awk, 쉘, C 언어를 모두 혼합해 넣은 듯한 인상을 주는 언어이다.

고차원 언어가 그러하듯 변수를 사용하기 위해 먼저 선언할 필요가 없다. 앞서 알아 본 C 언어 사용자의 고질적인 실수(포인터에 malloc하지 않고 자료 저장하기)는 걱정하지 않아도 되도록 만들었다. 뭔가 저장하고 싶으면 변수를 하나 만들어서 저장한다. 숫자를 저장했다 문자열을 저장할 수도 있다. C 또는 C++ 언어가 정적 자료형의 언어라면 펄은 동적 자료형을 갖춘 실전 언어이다.

$a = 3;
$a = "Linux";

예에서는 스칼라 변수 a 에 3 이라는 숫자 값을 넣었다고, 바로 다음에는 Linux라는 문자열 값을 넣고 있다. 이런 의미에서 펄은 수많은 언어의 장점을 취하고 단점을 없앤 언어라고 말하기도 한다.

PERLPractical Extraction & Reporting Language의 약자로서 실전에 있어 자료를 추출하고 그에 의거한 보고서를 작성하는데 특별한 능력을 발휘하는 언어이다. 이러한 펄은 주로 사용자의 입력을 받아 처리하는 1) CGI 프로그램 분야에서 맹위를 떨쳐 왔다. 또한 시스템의 상황 자료를 모아 그에 적절한 대처를 하는 2) 시스템 스크립트 분야에서 많이 사용하였다.

펄은 단순한 프로그래밍 언어 이상의 문화를 형성하고 있다. 개발자이며 뛰어난 저술가, 연설가인 Larry Wall 씨의 독특한 지휘력과 펄 개발자들의 열의가 합쳐져 방대하고 탄탄한 공동체를 이루고 있다. Larry Wall 씨는 펄을 "최초의 포스트 모던"한 언어라고 표현한다. 사실 펄은 그 이전의 C, C++ 등 모던한 언어와는 전혀 다른 무엇인가를 보여 주고 있다.

나중에 펄에 대하여 더 많은 관심을 갖고 파 들어가 보면 알겠지만, 펄은 CGI, 시스템 프로그래밍 외에도 거의 모든 분야(GUI 프로그래밍까지)를 망라할 수 있는 프로그래밍 언어라는 사실에 놀라게 된다.

펄 스크립트의 모습

#!/usr/bin/perl

while () {
        print $_;
}

위 예제는 표준입력(STDIN)으로부터 한 줄씩 읽어 그대로 표준 출력으로 내보내는 cat 명령과 같은 스크립트이다. 이런 기능을 C 언어로 만든다고 생각해 보라.

펄은 변수 이름 앞에 $, @, % 등을 붙이는 독특한 규칙을 가지고 있다. 이런 규칙은 쉘 스크립트나 다른 스크립트 언어에서 가져온 것이다. C 언어나 파이썬(Python) 언어를 즐겨 사용하는 사람은 변수 앞에 붙는 이러한 특수 문자를 싫어하기도 한다.

앞의 스크립트를 조금 더 확장시켜 보겠다. 유닉스 쉘 스크립트나 설정 파일에서 일반적으로 # 로 시작하는 행은 주석으로 간주하여 무시한다. 그러한 관행에 따라 입력행에서 # 로 시작하는 줄은 무시하는 스크립트를 만들어 보면 다음과 같다.

#!/usr/bin/perl

while (<>) {
	next if /^#/;
        print;
}

펄은 자기 고유의 강력한 정규표현식(RegEx)을 가지고 있어 텍스트 처리에 능하다. 위에서 /^#/ 라는 표현은 맨 처음 문자가 # 인지 판별하는 정규표현식이다.

진짜로 펄다운 표현 중 하나는 다음과 같은 것이다.

open(LILO, '/etc/lilo.conf') or die "Shit! I can't open it";

위 내용을 영어처럼 읽으면 된다. '/etc/lilo.conf'를 연다. 만약 그렇지 못하면 죽는다!

open(LILO, ') {
    print;
}

펄에 익숙해 지면 생각이 진행되는 대로, 코드를 신속하게 짜 나갈 수 있다. 그런 의미에서 펄을 프로토타입 잡기(prototyping) 언어라고도 한다.

펄 자료형

펄은 동적 자료형을 갖는다. 따라서 구분법이 C 언어와는 다르다.

  • 스칼라
    $a = 1;
    $b = "Linux";
  • 배열
    @a = (0, 1, 2, 3);
  • 해쉬(또는 연관 배열)
    %a = ( 'a' => 'apple', 'b' => 'banana', 'k' => 'kiwi' );

영어로 생각하자면, $를 "the", @를 "these", "those"라고 보면 된다. $는 단수, @는 복수이다.

$days		# days라는 변수의 간단한 스칼라 값
$days[28]	# @days 배열의 29 번째 원소
$days{'Feb'}	# %days 해쉬의 'Feb' 키에 연관된 값
$#days		# @days 배열의 마지막 인덱스 값

@days		# ( $days[0], $days[1], ..., $days[n] )
@days[3,4,5]	# @days[3..5]
@days{'a','c'}	# ( $days{'a'}, $days{'b'} )

%days		# ( key1, value1, key2, value2, ..., ... )

%test = ( 'a', 1, 'b', 2, 'c', 3 );
%test = ( 'a' => 1, 'b' => 2, 'c' => 3 );

%test 해쉬를 초기화할 때, 첫번째 단순 배열 표현보다는 키/값의 관계를 분명하게 보여 주는 => 표현이 더 보기 좋다.

펄을 잘 사용하려면 배열과 해쉬를 능숙하게 사용해야 한다!!!

그리고 이 점을 분명히 알아야 한다. @a 배열의 첫번째 값을 알려면 $a[0]라는 표현을 쓴다. @a[0]이 아니다! $, @, % 등은 최종적인 값의 형태를 지시한다.

%a 해쉬에서 'k' 라는 키(key)에 해당하는 값을 얻으려면 마찬가지로 %a{'k'}가 아니라 $a{'k'}라고 적는다.

배열에 대해서는 []를 사용했고 해쉬에 대해서는 {}를 사용한 점 잘 기억하자.

고난위로 진전하면, 파일 핸들, 참조 등의 개념을 익혀야 한다.

타입글롭(Typeglob)은 전체 심볼 테이블을 저장하는 자료형이다. 쉘에서 glob 이라는 표현을 사용하는 것을 본 적 있는가? 타입글롭에 대해서는 * 문자를 사용한다.(충분히 납득 가능한 문자가 아닌가?)

전체 심볼 또는 파일 핸들 등을 함수에 전달할 때, 타입글롭을 사용한다.

앞에서 와 같은 표현을 본 적 있을 것이다. 이것이 바로 파일 핸들이다. 파일 핸들을 변수에 저장할 때는 다음과 같이 한다. STDIN, STDOUT, STDERR 는 각각 표준 입력, 표준 출력, 표준 에러에 대한 파일 핸들이다.

*this = *that;

위 예는 일괄적으로 $this가 $that을 가리키고, @this가 @that을 %this가 %that을 가리키는 별명이 되도록 해 준다.

다음은 STDOUT 파일 핸들을(정확히 말해 참조) $fh 라는 스칼라 변수에 저장하여 STDOUT 대신 사용할 수 있다는 사실을 보여 준다.

$fh = *STDOUT;
$fh = *STDOUT;

print STDOUT "Hin";
print $fh "Hin";

펄에 익숙하지 않을 때에는 이런 모든 것이 사람을 헷갈리게 만든다. 때로는 펄을 포기하게 만드는 요인이기도 하다.

독특한 변수 표현

펄에서는 $_ 와 같이 $, @, % 문자와 특수 문자로 이루어진 특별한 변수들이 존재하며 이를 상당히 많이 사용한다. 루프 등에서 변수를 사용하지 않으면 자동으로 $_ 변수에 값을 넣는다. $$는 프로세스 ID 이며 $^O는 운영체제 이름을 담고 있다.

예를 들어, @_ 배열 변수는 서브 루틴을 호출할 때 전달한 인수 배열이 된다. 서브 루틴에서는 @_ 변수의 크기를 보고 몇 개의 인수가 전달되었는지 알 수 있다.

$#name은 @name 배열의 마지막 첨자를 반환한다. 배열의 첨자는 하나의 스칼라 값이므로 @# 라고 적지 않고 $# 라고 적은 것에 유의하자.

@array = (0..100);	# 0 부터 100 로 이루어진 배열

$a = $#array;		# @array의 마지막 첨자, 즉 100
$b = @array;		# @array의 크기, 즉 101

위에서 @array 배열을 $b라는 스칼라 변수에 대입하면 펄은 알아서 "상황에 맞게" 배열의 크기 값을 스칼라 변수에 대입한다. 펄은 이와 같이 같은 하나의 변수에 대해서도 상황(Context)에 따라 합리적인, 적절한 변환을 해 준다. 이 기능이 여러분을 때로는 편리하고 때로는 혼란스럽게 만들 것이다.

기본 문법

펄은 변수를 필요할 때 즉시 사용하므로 기본적으로 별다른 선언문을 필요로 하지 않는다. 펄에서 선언이 필요한 것은 딱 두 가지, 보고서 형식과 서브 루틴 뿐이다. 초기화되지 않은 변수는 상황에 따라, null 또는 0 이라는 값을 갖는다.

펄은 변수를 아무 때나 사용할 수 있으므로 defined를 사용하여 한 번도 사용하지 않은 변수인지 아닌지 판별 가능하다.

if ( defined($no) ) {
    print "Definedn";
} else {
    print "Undefinedn";
}

펄의 주석 지시 문자는 # 이다. C 언어나 C++ 언어 스타일의 주석을 사용해서는 안된다.

모든 문장(statement)은 C 언어에서처럼 세미 콜론(;)으로 끝마친다.

하나의 범위(scope)에 속하는 복합 문장은 블럭(block)을 형성한다. 블럭은 중괄호({})를 사용하여 구분한다.

제어문으로는 if, while, for, 그리고 C 쉘에서 볼 수 있는 foreach 등이 있다.

다음은 전형적인 while 루프이다. 괄호 안의 조건식이 0, "", "0" 이 아닌 동안, 블럭 내용을 실행한다.

# 라벨이 없는 평범한 루프
while () {
	...
	if (/^#/) { next; }
	...
}

# 라벨이 있는 루프
LINE: while () {
	...
	while (1) {
		...
		if ($i == 0) { last LINE; }
		...
	}
	...
}

두번째 예처럼 while 루프 등의 앞에 라벨(Label)을 붙일 수 있다. 라벨은 여러 개의 루프가 중첩되어 있을 때, C 언어의 continue에 해당하는 next, break에 해당하는 last에서 원하는 루프로 돌아가거나 단 번에 빠져 나갈 수 있도록 해 준다. redo는 조건식을 실행하지 않고 루프 블럭을 시작한다.

보통 C 언어에서는 goto 문을 사용하지 않으면 안되는 상황을 아주 간단히 만들어 준다. 그러나 남발하면 실행 흐름을 제대로 파악할 수 없게 되므로 주의하자.

다음은 C 언어와 비슷한 구조의 for 루프이다.

for ($i = 0; $i < 100; $i++) {
	print $i, "n";
}

foreach my $i (@numbers) {
	print $i * 2, "n";
}

foreach (@numbers) {
	print $_ * 2, "n";
}

C 쉘 스크립트를 짜 본 사용자라면 익숙할, 두번째 foreach는 괄호 안의 값을 순차적으로 $i 에 대입하여 블럭을 실행한다. 세번째처럼 my $i 카운터 변수를 생략하면 $_ 변수에 대입한다.

어떤 단순한 일을 100 번 반복하고 싶다면?

for (0..99) {
	# statement
}

0..99 라는 표현을 눈여겨 보자.

별도의 switch 구문은 없다. 다음은 블럭과 if를 사용하여 switch 문을 흉내낸다.

SWITCH: {
    if (/^abc/) { $abc = 1; last SWITCH; }
    if (/^def/) { $def = 1; last SWITCH; }
    if (/^xyz/) { $xyz = 1; last SWITCH; }
    $nothing = 1;
}

서브 루틴

두 인수를 비교하여 큰 값을 반환하는 max 서브 루틴을 정의한다.

sub max {
        my ($a, $b) = @_;
        if ($a > $b) { return $a; }
        else { return $b; }
}

print max(2, 3);

C 언어에서와는 달리 서브 루틴 정의 시 인수 목록이 없다.

서브 루틴은 호출 시 적은 인수들로 이루어진 단일 배열 변수를 받는다. 그 변수의 이름은 @_ 이다. 그리고 반환할 때에는 C 언어와 달리 하나 이상의 값으로 이루어진 배열 변수값을 전달할 수 있다.

sub swap {
        my ($a, $b) = @_;
        return ($b, $a);
}

$c = 1;
$d = 2;

($c, $d) = swap($c, $d);

print $c, $d;

서브 루틴이 등장했으니 mylocal에 대하여 알아보겠다.

변수 앞에 my 를 적으면 오로지 그 서브 루틴 또는 블럭에서만 보이는 자동 변수가 된다. local 을 적으면 그 서브 루틴 뿐 아니라 호출한 하위 서브 루틴에서도 보이는 변수가 된다.

객체 지향 펄 : 모듈(Module)

펄 버전 5 부터는 객체 지향 스타일 일부를 지원하기 시작했다. 어떠한 특별한 기능을 별도의 파일에 두고 다음처럼 사용한다. 예를 들어, 전형적인 CGI 프로그램은 다음처럼 시작한다.

use CGI;

그러면 예제 CGI 프로그램을 하나 보겠다.

#!/usr/bin/perl

use CGI;

$q = new CGI;

print $q->header(),
      $q->start_html(-title='Yaho'),
      $q->h1('H1 Level'),
      '본문 내용',
      $q->end_html();

use CGI, new CGI 라는 표현에 주목하자. CGI 모듈은 CGI에 관련된 모든 기능을 제공하고 있다. CGI 모듈에 조금에 익숙해지면 CGI 프로그램을 만드는 일은 식은 죽 먹기이다.

요즘 유행하는 SQL 데이터베이스와의 연결 기능에서 펄을 능가할 자가 없다고 봐도 좋다. 펄은 오래 전부터 DBI라는 공통 데이터베이스 인터페이스를 표준화하고 있다.

use DBI;

$dbh = DBI->connect("DBI:mysql:database=test", $user, $pass);
$sth = $dbh->prepare("SELECT * FROM guestbook");
$sth->execute;

...

DBI를 사용할 때에는 마찬가지로 use DBI; 부터 시작한다.

CGI 모듈과 DBI 모듈을 잘 사용하면, 정말로 훌륭한 인터넷 프로그래머라는 평가를 받게 될 것이다.

펄 학습하기

  • 펄 문법
  • 펄 자료형
  • 펄 서브루틴
  • 펄 연산자
  • 펄 정규표현식
  • 각종 펄 모듈

특히 펄에서는 정말로 다양한, 편리한 연산자를 많이 제공한다. 정규표현식을 알면 펄의 반은 정복한 것이나 다름없다.

실전 프로그래밍에서는 얼마나 많은 펄 모듈을 자유롭게 사용(use)하는가에 따라 작업 효율이 많이 달라진다.

펄 스타일 익히기

C/C++ 언어를 이미 사용해 본 사람은 펄을 쉽게 배울 수 있다. 그리고 펄의 기본적인 요소만 알면 금방 코딩을 시작할 수 있다.

그러나 펄 관련 뉴스그룹 같은 곳에 가 보면, "진짜" 펄 프로그래머들이 있으며 그들의 스타일이 상당히 C/C++ 스타일과 다르다는 것을 알 수 있을 것이다. 여러분이 C/C++ 스타일로 3-4 줄에 할 일을 펄 도사들이 어떻게 한 줄로 축약하여 사용하는지 알아 보는 것도 재미있다.

펄의 유명한 모토가 있다! There's More Than One Way To Do It! 이를 줄여서 TMTOWTDI 라고 한다.

참고 서적

기본적으로 오렐리 출판사에서 나온 모든 펄 서적이 펄의 바이블이다. perl.oreilly.com을 방문하라.

  • Learning Perl (초보자용)

  • Programming Perl (언어적 측면의 접근)
  • Advanced Perl Programming (실전 펄 프로그래밍)
  • Programming the Perl DBI (펄과 DB 연동 중심)

참고 사이트

펄은 훌륭한 책도 많고 오랜 경험을 통해 문서화도 잘 되어 있다. 실제로 거의 모든 해결책을 펄.COM 사이트에서 구할 수 있다!

이 문서에 대하여

이 문서는 [프리 매뉴얼(Free Manual)]로서 어떤 사람이든, 어떤 매체든, 그것이 수정 형태든 원본 형태든 상관없이 인용하거나 복제할 수 있다.

이 문서의 홈 페이지는 다음과 같다.

http://kldp.org/~yong/programming/intro/

이만용 (Bryan Lee)
manyong@gnu.org