die와 실질적인 프로그램의 중지장소

전종필

die함수를 사용할 때 인수를 주지 않거나, 인수의 맨 뒤에 "\n" 문자를 붙여주지 않으면 "... at myprog.pl line 123"과 같은 내용을 출력합니다.
이 내용은 디버깅을 할 때 상당히 유용한 내용입니다. 출력된 내용에 따라, 해당 파일의 해당 라인을 들여다 보면 되니까요.
하지만, 이 내용이 실질적인 에러 발생지점이라고 보기가 어려운 때가 많이 있습니다. 다음 예를 보시기 바랍니다. (다음 프로그램이 꽤 긴 것이라고 생각합시다.)

# prog.pl
require 'lib.pl';
my $file = shift @ARGV;
my $txt = readFile($file); # 라이브러리에 있는 서브루틴
my( $logfile ) = ( $text =~ /\blogfile=(\S+)/ );
my( $textfile ) = ( $text =~ /\btextfile=(\S+)/ );
# 중간 500줄 생략 ^^
my $logtxt = readFile($logfile);
# 중간 300줄 생략 ^^
my $texttxt = readFile($logfile);
# 이러고 저러고...
# 끝.
다음은 라이브러리입니다.

# lib.pl
sub readFile {
	my $file = shift;
	my $txt;
	if( open(FILE,$file) ) {
		local $/ = undef;
		$txt = ;
		close FILE;
	}
	else {
		die "파일읽기 오류($file): $!";
	}
	$txt;
}
만일 prog.pl을 실행하던 중 파일 이름이 틀리거나, 잘 못된 이름이어서 readFile 서브루틴에서 open이 실패된다면, 프로그램은 다음과 유사한 내용을 출력하고 종료합니다.

파일읽기 오류(filename): No such file or directory at lib.pl line 11.
내용은 prog.pl이 아닌 lib.pl에서 실제로 die가 호출된 지점을 알려줍니다. 하지만, 우리가 원하는 내용은 my $txt = readFile($file);my $logtxt = readFile($logfile);, 혹은 my $texttxt = readFile($logfile);이 호출된 어떤 지점의 라인 번호일 것입니다. 그 것을 알아내기 위해서 caller라는 함수를 사용합니다. 그리고 lib.pl을 다음과 같이 수정합니다.( else { die.. } 부분)

# lib.pl
sub readFile {
	my $file = shift;
	my $txt;
	if( open(FILE,$file) ) {
		local $/ = undef;
		$txt = ;
		close FILE;
	}
	else {
		my( $package, $filename, $linenum, $subroutine ) = caller; # ``caller 0''과 같음
		die "파일읽기 오류(File: $file): $! at $filename line $linenum\n";
	}
	$txt;
}
caller함수를 호출하면 위와 같이 현재의 서브루틴을 호출한 부분에 대한 정보를 반환합니다.
만일 여러 단계의 서브루틴 호출이 있었고, 그 호출 내용을 모두 알아야 한다면, caller에 인수로 0부터 1씩 증가시켜가며,

	else {
		my $at;
		my $i = 0;
		my( $package, $filename, $linenum, $subroutine )
		while( ( $package, $filename, $linenum, $subroutine ) = caller($i++) ) {
			$at .= " /$filename($linenum)";
		}
		die "파일읽기 오류(File: $file): $! at $at\n";
	}
하면, 모든 파일이름과 라인 번호를 얻을 수 있습니다. 
Comments