Perl 함정 모음
Perl 함정 모음 (perltrap)
다른 언어에서 Perl로 넘어오면 '이건 왜 안 되지?' 하는 순간들이 꼭 생겨요. perltrap은 그런 함정들을 언어별로 정리한 문서예요. awk, C/C++, JavaScript, sed, shell에 익숙한 사람들이 Perl로 옮겨올 때 가장 자주 걸리는 지점을 콕 집어 알려줘요.
가장 큰 함정들 (DESCRIPTION)
- 가장 큰 함정은
use warnings또는-w스위치를 잊어버리는 거예요 (warnings와 perlrun의-w참고). - 두 번째로 큰 함정은 프로그램 전체를
use strict아래에서 돌릴 수 있게 만들지 않는 거예요. - 세 번째로 큰 함정은 이번 Perl 버전의 변경 목록을 읽지 않는 거예요 (
perldelta참고).
awk 사용자를 위한 함정 (Awk Traps)
awk에 익숙한 사용자라면 특히 다음을 주의하세요:
- Perl 프로그램은 입력 줄마다가 아니라 딱 한 번 실행돼요. 암묵적 루프는
-n이나-p로 만들 수 있어요. use English;으로 불러온English모듈은 특수 변수(예:$/)를 이름(예:$RS)으로 참조할 수 있게 해줘요, 마치 awk처럼요 (자세한 건 perlvar).- Perl에서는 모든 단순 문장 뒤에 세미콜론이 필요해요 (블록 끝은 예외). 줄바꿈은 문장 구분자가 아니에요.
if와while에는 중괄호가 필수예요.- Perl에서 변수는
$,@,%로 시작해요. - 배열 인덱스는 0부터 시작해요.
substr()와index()의 문자열 위치도 마찬가지예요. - 배열이 숫자 인덱스를 쓸지 문자열 인덱스를 쓸지 결정해야 해요.
- 해시 값은 단순히 참조한다고 저절로 생기지 않아요.
- 문자열 비교를 쓸지 숫자 비교를 쓸지 결정해야 해요.
- 입력 줄을 읽는다고 자동으로 쪼개주지는 않아요. 직접 배열로
split해야 해요. 그리고split()연산자의 인자는 awk의 것과 달라요. - 현재 입력 줄은 보통
$0이 아니라$_에 있어요. 그리고 대개 줄바꿈이 제거돼 있지 않아요. ($0은 실행된 프로그램의 이름이에요.) perlvar 참고. $<digit>은 필드를 가리키지 않아요 — 마지막 매치 패턴이 잡아낸 부분 문자열을 가리켜요.print()는$,와$\를 설정하지 않는 한 필드·레코드 구분자를 추가하지 않아요.English모듈을 쓴다면$OFS와$ORS를 설정하면 돼요.- print 하기 전에 파일을 먼저 열어야 해요.
- 범위 연산자는
..이지 쉼표가 아니에요. 쉼표 연산자는 C와 동일하게 동작해요. - 매치 연산자는
=~이지~이 아니에요. (~는 C와 같이 1의 보수 연산자예요.) - 지수 연산자는
**이지^이 아니에요.^는 C와 같이 XOR 연산자예요. (이쯤 되면 awk가 C와 근본적으로 호환되지 않는다는 느낌이 들죠.) - 결합(연결) 연산자는
.이지 널 문자열이 아니에요. (널 문자열을 쓰면/pat/ /pat/을 파싱할 수 없게 돼요. 세 번째 슬래시가 나눗셈 연산자로 해석되거든요. 토크나이저는 사실/,?,>같은 연산자에 대해 약간 문맥에 민감해요. 그리고 사실.자체도 숫자의 시작이 될 수 있어요.) next,exit,continue키워드는 다르게 동작해요.- 다음 변수들은 다르게 동작해요:
Awk Perl
ARGC scalar @ARGV (compare with $#ARGV)
ARGV[0] $0
FILENAME $ARGV
FNR $. - something
FS (whatever you like)
NF $#Fld, or some such
NR $.
OFMT $#
OFS $,
ORS $\
RLENGTH length($&)
RS $/
RSTART length($`)
SUBSEP $;
$RS를 패턴으로 설정할 수 없어요. 문자열만 가능해요.- 의심스러우면 awk 구문을
a2p에 돌려서 어떤 결과가 나오는지 보세요.
C/C++ 프로그래머를 위한 함정 (C/C++ Traps)
if와while에는 중괄호가 필수예요.else if가 아니라elsif를 써야 해요.- C의
break와continue는 Perl에서 각각last와next가 돼요. C와 달리 이들은do { } while구문 안에서는 동작하지 않아요 (perlsyn의 "Loop Control" 참고). - switch 문은
given/when이라고 불러요. perl 5.10 이상에서만 사용 가능해요 (perlsyn의 "Switch Statements" 참고). - 변수는
$,@,%로 시작해요. - 주석은
/*나//가 아니라#으로 시작해요. Perl은 C/C++ 주석을 나눗셈 연산자·끝나지 않은 정규식·defined-or 연산자로 해석할 수 있어요. - 어떤 것의 주소를 취할 수는 없어요. 다만 Perl에는 비슷한 연산자로 참조(reference)를 만드는 백슬래시가 있어요.
ARGV는 대문자여야 해요.$ARGV[0]은 C의argv[1]이고,argv[0]은$0에 들어가요.link(),unlink(),rename()같은 시스템 호출은 성공 시 0이 아닌 값을 반환해요. (단,system()은 성공 시 0을 반환해요.)- 시그널 핸들러는 숫자가 아니라 시그널 이름을 다뤄요. 시스템에서 그 이름을 찾으려면
kill -l을 쓰세요.
JavaScript 프로그래머를 위한 함정 (JavaScript Traps)
- Perl에서 이항
+는 항상 덧셈이에요.$string1 + $string2는 두 문자열을 숫자로 변환한 뒤 더해요. 두 문자열을 이어 붙이려면.연산자를 쓰세요. - 단항
+연산자는 Perl에서 아무것도 하지 않아요. 문법적 모호함을 피하기 위해 존재할 뿐이에요. for...in과 달리 Perl의for(일명foreach)는 좌변에 임의의 표현식을 허용하지 않아요. 반드시 변수여야 해요:
for my $variable (keys %hash) {
...
}
그리고 여기서 keys를 빼먹지 마세요. foreach my $kv (%hash) {}는 키와 값을 번갈아 순회해서 대체로 쓸모없어요 ($kv는 키, 그다음 값, 그다음 또 키... 가 되니까요).
- 배열의 인덱스를 순회하려면
foreach my $i (0 .. $#array) {}를 쓰세요.foreach my $v (@array) {}는 값을 순회해요. - Perl은
if,while,foreach다음에 중괄호를 요구해요. - Perl에서
else if는elsif예요. ? :는 대입보다 우선순위가 높아요. JavaScript에서는
condition ? do_something() : variable = 3
처럼 써서 조건이 거짓일 때만 변수에 대입할 수 있어요. Perl에서는 괄호가 필요해요:
$condition ? do_something() : ($variable = 3);
아니면 그냥 if를 쓰면 돼요.
- Perl은 문장을 구분하려면 세미콜론이 필요해요.
my로 선언한 변수는 선언 뒤의 코드에만 영향을 줘요.$x = 1; my $x;라고 쓰고 첫 대입이 같은 변수에 영향을 주길 기대하면 안 돼요. 그 대입은 바깥 스코프에서 선언된$x나 전역 변수에 가버려요. 또, 변수는 다음 문장부터 보여요. 즉my $x = 1 + $x에서 두 번째$x는 이전에 선언된 것을 가리켜요.my변수는 현재 함수가 아니라 현재 블록에 스코프돼요.{my $x;} $x;라고 쓰면 두 번째$x는 블록 안에서 선언된 것을 가리키지 않아요.- 객체의 멤버를 변수처럼 접근할 수는 없어요.
with(object) { method() }에 가장 가까운 Perl 대안은for로$_를 객체에 별칭하는 거예요:
for ($object) {
$_->method;
}
- 메서드가 호출되는 객체나 클래스는 메서드 인자 중 하나로 넘어와요. 별도의
this값으로 넘어오지 않아요.
sed 사용자를 위한 함정 (Sed Traps)
- Perl 프로그램은 입력 줄마다가 아니라 딱 한 번 실행돼요. 암묵적 루프는
-n이나-p로 만들어요. - 치환(substitution)의 역참조는
\이 아니라$를 써요. - 패턴 매칭 메타문자
(,),|는 앞에 백슬래시가 없어요. - 범위 연산자는 쉼표가 아니라
..예요.
shell 사용자를 위한 함정 (Shell Traps)
- 백틱 연산자는 명령 안에 작은따옴표가 있어도 상관없이 변수 보간을 해요.
- 백틱 연산자는 csh와 달리 반환값을 변환하지 않아요.
- 셸(특히 csh)은 각 명령 줄에 여러 단계의 치환을 해요. Perl은 큰따옴표, 백틱, 꺾쇠괄호, 검색 패턴 같은 특정 구문에서만 치환해요.
- 셸은 스크립트를 조금씩 해석해요. Perl은 실행 전에 전체 프로그램을 컴파일해요 (컴파일 시점에 실행되는
BEGIN블록은 제외). - 인자는
$1,$2가 아니라@ARGV로 접근해요. - 환경 변수가 자동으로 개별 스칼라 변수로 제공되지는 않아요.
- 셸의
test는 문자열 비교에=,!=,<등을, 숫자 비교에-eq,-ne,-lt등을 써요. Perl은 그 반대예요. 문자열 비교에eq,ne,lt를, 숫자 비교에==,!=,<등을 써요.
Perl 프로그래머를 위한 함정 (Perl Traps)
- 많은 연산이 리스트 컨텍스트와 스칼라 컨텍스트에서 다르게 동작한다는 걸 기억하세요 (perldata 참고).
- 가능하면 bareword를 피하세요. 특히 전부 소문자인 것은요. bareword가 함수인지 문자열인지 보기만으로는 알 수 없어요. 문자열엔 따옴표를, 함수 호출엔 괄호를 붙이면 절대 헷갈리지 않아요.
- 겉보기만으로 어떤 내장 함수가 단항 연산자인지(
chop(),chdir()) 리스트 연산자인지(print(),unlink()) 구분할 수 없어요. (프로토타입이 없는 한, 사용자 정의 서브루틴은 리스트 연산자일 수밖에 없고 단항은 될 수 없어요.) perlop과 perlsub 참고. - 어떤 함수는
$_나@ARGV를 기본으로 쓰는데, 그럴 것 같지 않은 다른 함수는 그렇지 않다는 걸 기억하기 어려워요. <FH>구문은 파일핸들의 이름이 아니라 그 핸들에 대한 readline 연산이에요. 읽은 데이터는 파일 읽기가 while 루프의 유일한 조건일 때만$_에 대입돼요:
while (<FH>) { }
while (defined($_ = <FH>)) { }..
<FH>; # data discarded!
=가 필요한 곳에=~를 쓰지 말고, 그 반대도 마찬가지예요. 이 둘은 상당히 달라요:
$x = /foo/;
$x =~ /foo/;
do {}구문은 루프 제어를 쓸 수 있는 진짜 루프가 아니에요.- 로컬 변수는 가능하면 언제나
my()를 쓰세요 (단, 쓸 수 없는 곳은 perlform 참고).local()을 쓰면 실제로는 전역 변수에 로컬 값을 주는 것이라, 동적 스코프의 예상치 못한 부작용에 노출될 수 있어요. - 모듈에서 내보낸(exported) 변수를 localize하면 그 내보낸 값은 바뀌지 않아요. 로컬 이름은 새 값의 별칭이 되지만, 바깥 이름은 여전히 원래 값의 별칭이에요.
언제나 그렇듯이, 이 중 어떤 것이 공식적으로 버그로 선언되면 고쳐져서 함정 목록에서 빠질 거예요.
더 알아보기 (Learn more)
warnings,perlrun의-w— 경고 켜기strict— 최소한의 안전장치perldelta— Perl 버전 변경 사항a2p— awk 프로그램을 Perl로 변환