Perl이란 무엇인가?

Perl이란?

Perl은 여러분의 작업을 이루어지게끔 해 주는 언어이다.

물론, 여러분의 주된 일이 프로그래밍이라면, 이론적으로 말하는 임의의 “완벽한” 프로그래밍 언어로도 주어진 일을 할 수 있을 것이다. 그러나 경험에 비추어 볼 때, 여러 프로그래밍 언어들은 ‘무엇을 할 수 있는가’라는 측면에서는 그리 큰 차이가 없지만 ‘얼마나 쉽게 그 일을 할 수 있게 하는가’라는 측면에서는 언어마다 많은 차이가 있다. 극단적인 예로, 소위 “제 4세대 언어”로 불리는 몇몇 언어는 몇 가지 일은 아주 쉽게 할 수 있지만, 그 밖의 일에 대해서는 거의 불가능한 경우도 있다. 또 다른 예로, 널리 알려진 몇 가지 “매우 강력한” 언어조차도 모든 일을 해결하기에는 똑같이 어렵다.

그런 면에서 Perl은 다르다. Perl은 어려운 일을 불가능하게 만들지 않으면서 쉬운 일을 쉽게 해결할 수 있도록 디자인되었다.

그러면 도대체 위에서 언급한 “쉬운 일”이란 무엇인가? 여러분이 일상적으로 하는 일이다. 여러분은 숫자나 텍스트, 파일과 디렉토리, 컴퓨터와 네트워크, 컴퓨터 등을 손쉽게 다룰 수 있는 언어를 원할 것이다. 그러한 언어는 외부 프로그램을 실행하고 그 출력 결과를 검색하는 것이 쉬워야 할 것이다. 또한, 자신의 출력 결과를 다른 프로그램에 전달하여 무엇인가 특별한 일을 쉽게 할 수 있도록 해야 할 것이다. 그리고 사용자의 프로그램을 개발하거나 변경, 디버그하기에도 쉬워야 할 것이며, 컴파일하고 실행하는 것 역시 쉬워야 할 것이다. 물론, 현재 사용되는 어떤 운영 체제(OS, Operating System) 에도 쉽게 이식될 수 있어야 한다.

Perl이 바로 그러한 언어이다. Perl은 그 외에도 많은 일을 할 수 있다.

비록 Perl이 초기에 UNIX 운영체제(혹은 무수한 UNIX 변종)에서의 “glue” 언어로 디자인되었지만, 그 밖에 MS-DOS, VMS, OS/2, Plan 9, Macintosh, Windows등 다양한 운영 체제에서 실행된다. Perl은 현재 사용 가능한 현존하는 프로그래밍 언어 중 가장 이식성이 좋은 언어에 속한다. C언어의 경우 서로 다른 운영 체제에서 동작하도록 프로그래밍하려면 각 운영 체제에 해당하는 수 많은 #ifdef을 첨가해야 한다. 쉘 프로그래밍의 경우, 각 운영 체제의 버전에 따라 조금씩 다른 쉘 명령어의 문법을 알아야 하고, 가능한 한 이식성을 높이기 위해서는 서로 다른 환경에서 공통적으로 동작하는 최소한의 명령어를 찾아서 사용해야 한다. Perl은 C와 쉘 프로그래밍의 장점을 채용하면서도 위와 같은 문제점은 발생하지 않는다. Perl의 폭발적인 성장은 이러한 문제점을 해결하기를 갈망했던 이전의 UNIX 프로그래머의 성원에 힘입은 바 크다. Perl이 UNIX 프로그래머한테 증류수나 오아시스로 여겨지는 데 비해, Web 프로그래머들은 기존의 Windows에서 동작하던 스크립트를 수정하지 않고 그대로 UNIX 서버에서 실행할 수 있다는 점이 매력적이었다.

비록 Perl이 시스템 프로그래머와 WWW 개발자들에게 더욱 인기 있는 언어이긴 하지만, 그 외에도 다양한 분야에서 Perl이 사용되고 있다. Perl은 더 이상 단순히 텍스트 처리를 위한 언어가 아니다. Perl은 디버거, 프로파일러(profiler), 크로스 레퍼런서(cross-referencer), 컴파일러, 인터프리터, 라이브러리, 편집기, 그리고 다른 프로그래밍 언어가 가진 많은 기능을 포함하는 풍부한 소프트웨어 개발 환경을 갖춘 일반적인 용도의 프로그래밍 언어로 발전했다. Perl은 우주 공학에서 분자 생물학에 이르기까지, CAD/CAM에서 문서 처리, 데이터베이스에서 클라이언트/서버 네트워크 관리에 이르기까지 거의 모든 분야에서 사용되고 있다. Perl은 DNA 구조 순서를 다루거나, WWW 페이지를 만드는 등 많은 양의 데이터를 분석하고 변환하는 작업을 신속하게 수행하고자 하는 경우에 많이 사용된다. 실제로 Perl에 관한 유머 중 하나로, 만약 이 다음에 주식 시장에서 큰 사고가 발생한다면 아마 그것은 Perl 스크립트의 버그 때문일 것이다라는 정도다.

Perl이 이처럼 성공한데에는 많은 이유가 있다.

그 중 하나는 Perl이 공짜라는 점과, 얼마든지 재배포할 수 있다는 점일 것이다. 그러나 그것만으로 Perl이 현재와 같이 널리 사용되고 있는 현상을 설명할 수는 없다. 많은 수의 공짜 소프트웨어가 널리 사용되지 못한 채 사라지고 있는 것이 현실이다. Perl의 장점은 공짜 소프트웨어라는 것 만이 아니다. Perl은 또한 재미있기도 하다. 사람들은 Perl로 프로그램을 작성하면서 자신이 생각하는 바를 자유롭게 표현할 수 있기 때문에, 그들 스스로가 창조적으로 작업을 할 수 있다고 생각한다. Perl을 사용하면, 필요에 따라 컴퓨터에서 실행되는 속도나 프로그래밍의 속도 어느 쪽으로 최적화할 수도 있으며, 장황하게 설명을 달거나 아니면 매우 간략한 형태로 기술할 수도 있고, 가독성을 높이거나 추후 유지보수를 고려하여 작성할 수도, 프로그램 코드의 재사용이나 이식성 측면을 고려할 수도 있다. 심지어는 남들이 알아보기 어렵게끔 최적화할 수도 있다.

Perl은 사용자에게 최대한의 자유를 제공한다. Perl은 매우 단순한 언어이면서 동시에 매우 풍부한 기능을 제공하는 언어이다. Perl은 다른 많은 언어의 장점을 따서 사용하기에 쉽도록 만들어진 언어이다. Perl의 그러한 면을 단순히 좋아하는 사람들에게는, Perl 은 Practical Extraction and Report Language이다. Perl의 그런 면을 사랑하는 이들에게는, Perl은 Pathologically Eclectic Rubbish Lister이다. 단순화주의자에게는 Perl이 기존의 언어와 중복되는 또 하나의 의미없는 연습으로 보일 지 모른다. 어차피 이 세상에는 약간의 단순화주의자는 있기 마련이니까.

Perl은 여러면에서 단순한 언어이다. 하나의 Perl프로그램을 컴파일하기 위해 다양한 특수 주문을 알아야 할 필요는 없다. 단지 쉘 스크립트처럼 실행하기만 하면 된다. Perl에서 사용하는 타입(type)이나 구조(structure)등은 이해하고 사용하기 쉽다. Perl은 데이터에 제한을 두지 않는다. 문자열이나 배열(array)등은 필요한 경우 메모리가 허용하는 한 얼마든지 그 크기를 늘릴 수 있도록 되어 있다. Perl만의 새로운 문법을 배워야 할 필요도 없다. Perl은 기존에 많이 사용되고 있는 다른 언어(C, sed, awk, 영어, 독일어)에서 많은 것을 빌려왔다. 실제로 다른 프로그래밍 언어를 사용하는 사람이라도 잘 작성된 Perl코드를 보면 해당 코드가 무슨 일을 하는 것인지 알 수 있을 정도이다.

가장 중요한 것은, 유용한 프로그램을 작성하기 위해 처음부터 Perl의 모든 것을 알 필요가 없다는 것이다. 처음에는 Perl의 최소한의 기능을 이용하여 프로그램을 작성하는 것을 배우면 된다. 비록 여러분이 작성한 프로그램이, 어린 아이가 말하는 것처럼 우습게 보일지라도 아무도 웃지 않는다. Perl의 많은 것은 자연어에서 빌려온 것으로서, 여러분의 문제를 해결할 수 있는 한 언어의 일부만을 사용하더라도 전혀 문제가 없다. Perl에서는 얼마나 유창하게 Perl의 문법을 구사하는가는 그리 중요하지 않다. 물론 유창하게 구사되지 않은 Perl프로그램을 작성한다고 해서 여러분한테 문법 경찰(language police)이 찾아가지도 않는다. 여러분의 상사가 여러분을 해고하기 전에 주어진 일을 할 수 있다면, 어떤 Perl 스크립트도 “제대로 된” 것이다.

비록 여러 측면에서 Perl이 단순한 언어라고 하지만, 동시에 Perl은 풍부한 기능을 제공하는 언어이며 여러분은 그런 것들에 대해 알아야 한다. 그렇게 함으로써 쉽게 해결할 수 없는 것들을 Perl을 이용해 해결할 수 있게 될 것이다. 여러분이 Perl이 할 수 있는 모든 것을 익히는 데 시간이 걸리겠지만, 나중에 여러분이 주어진 문제를 해결하기 위해 필요할 때 Perl의 다양한 기능을 사용할 수 있다는 것을 알면 기뻐할 것이다. 앞서 말했듯이 Perl은 쉘과 C에서 많은 기능을 따왔지만, 동시에 Perl은 sedawk이 할 수 있는 모든 일을 할 수 있다. 실제로, Perl 패키지에는 이전의 sedawk 스크립트를 Perl 스크립트로 변환하는 프로그램도 포함되어 있어서, 기존에 여러분에게 익숙한 기능이 Perl에서는 어떤 형식으로 구현되는지 볼 수 있다.

데이터 축약 언어(data-reduction language)나 파일을 탐색하기 위해, 많은 양의 텍스트를 검색할 때, 동적 데이타를 생성할 때, 주어진 데이터를 포매팅하여 보고서를 작성하는 경우에 한해서도 Perl은 풍부한 기능을 제공한다. 그러나 이 외에도 Perl은 파일 시스템 처리, 데이타베이스 관리, 클라이언트-서버 프로그래밍, 보안 프로그래밍, 웹 기반 정보 유지보수, 심지어 객체 지향 언어로서도 사용되고 있다.

Perl은 모듈을 쉽게 확장할 수 있도록 디자인되었다. Perl은 또한 사용자로 하여금 디자인, 프로그래밍, 디버그, 응용프로그램 개발을 신속하게 할 수 있도록 해 주며, 필요시 이들 응용 프로그램의 기능을 쉽게 확장할 수 있도록 해 준다. Perl을 다른 언어에 포함해 사용할 수 있으며, 반대로 다른 언어를 Perl안에 포함해 사용할 수도 있다. 모듈 임포테이션 메커니즘(module importation mechanism)을 이용하면 마치 이러한 것들이 Perl에 내장되어 있는 것처럼 사용할 수 있다. Perl의 객체 지향 요소는 객체 지향 외부 라이브러리를 통해 구현된 것이다.

그외 여러가지 방법으로 Perl은 여러분의 작업을 쉽게 해결하도록 도와준다. 쉘과 같이 엄격하게 해석되는, 한 번에 스크립트 내의 한 명령어를 해석하고 실행하는 언어와는 달리, Perl은 여러분의 프로그램 전체를 처음에 컴파일하여 중간 포맷(intermediate format)으로 만든다. 다른 컴파일러와 마찬가지로 Perl도 다양한 형태의 최적화를 수행하며, 문법적 오류나 잘못된 라이브러리 바인딩등의 경우에 사용자에게 오류 메시지를 즉시 보여준다. 이 과정에서 문제가 없으면, 중간 코드(intermediate code)를 인터프리터로 전달하여 실행하도록 한다.(혹은 필요한 경우 C나 바이트코드(bytecode)를 생성하도록 다른 모듈에 전달하기도 한다.) 이 모든 것이 복잡하게 들릴 지도 모르지만, Perl컴파일러와 인터프리터는 꽤 효율적으로 동작하기 때문에 일반적인 프로그램을 컴파일하고 실행하는 데에는 수 초의 시간밖에 소요되지 않는다. Perl의 다양한 fail soft기능(고장이나 오류가 발생하였을 때 그것을 한 부분에 국한시켜 전체 시스템이 마비되는 것을 방지하고 수행을 계속할 수 있도록 짠 프로그램: 역자 주) 덕분에, 신속하게 프로토타입(prototype)을 만들어야 하는 경우 Perl을 프로그래밍 언어로 사용하는 데 손색이 없다.

Perl은 또한 사용자의 프로그램을 보다 안전하게 동작하도록 해 준다. Privileged 모드에서 동작하는 경우, 특정 시스템 리소스에 접근하기 전에 사용자의 ID를 임시로 다르게 바꿀 수 있다. 또한 Perl은 어떤 데이타가 안전하지 못한 소스로부터 왔는지를 결정하고 위험한 동작을 사전에 예방하는 데이타 추적 메커니즘을 통해 만약의 보안 사고에 대비한다. 마지막으로 Perl은 사용자로 하여금 특별히 보호된 영역을 만들어 의심스러운 코드의 실행이나 위험한 동작을 안전하게 수행해 볼 수 있도록 해 준다. 이러한 기능은 시스템 관리자나 CGI 프로그래머에게 매우 유용하게 사용되고 있다.

이 세상을 구하기 위해 Perl을 배우든지, 혹은 단지 호기심 때문에, 혹은 여러분의 직장 상사가 하라고 해서 Perl을 배우고 있더라도 이 책은 여러분에게 Perl의 기초에서 복잡한 내용에 이르기까지 인도할 것이다. 비록 이 책이 여러분에게 프로그램을 작성하는 방법에 대해 가르쳐 주지 않더라도, 눈치 빠른 독자는 내용 중에서 여러 가지 중요한 부분을 건질 수 있을 것이다. 우리는 여러분에게 프로그래머의 세 가지 미덕을 갖추기를 권한다: 게으름(laziness), 참을성 없음(impatience), 그리고 자기 과신(hubris). 여러분이 이 책을 읽는 동안 다소 재미있는 부분을 발견하기를 바란다. 이러한 점들이 여러분에게 어필하지 않았더라도, Perl에 대해 공부하는 것은 여러분 이력에 도움이 된다는 것을 명심해 두기 바란다.

Perl 매뉴얼 페이지(Manpage)

Perl의 온라인 매뉴얼 페이지는 각 절로 분리되어 있어서 필요한 경우 수 백 페이지의 분량을 처음부터 끝까지 찾아보지 않고도 필요한 내용을 쉽게 찾을 수 있도록 되어 있다. 가장 최상위 매뉴얼 페이지는 perl이므로, UNIX 상에서 “man perl”하면 Perl의 매뉴얼 페이지를 볼 수 있을 것이다. 좀 더 자세한 내용에 대해서는 각각의 매뉴얼 페이지를 참조하면 된다. 예를 들어, Perl에서의 정규 표현식에 관한 매뉴얼 페이지를 보려면 “man perlre”하면 된다. 만약 man(1) 명령어가 동작하지 않는 경우에는 Perl 패키지에 포함되어 있는 perldoc 명령어를 사용하면 되며, 특별히 시스템 관리자가 일반적인 매뉴얼 페이지를 설치하는 것을 원하지 않는 경우에 모듈에 관한 문서를 참조하고 싶을 때 유용하다. 혹은 Perl의 매뉴얼 페이지를 HTML(hypertext markup language)포맷으로 시스템 관리자가 설치했을 경우에는 WWW 브라우저로 매뉴얼 페이지를 볼 수 있다.

Usenet Newsgroup

Perl에 관한 뉴스그룹은 때때로 혼잡하긴 하지만 Perl에 관한 무궁무진한 정보가 있는 곳이다. comp.lang.perl.announce는 새로운 버전의 Perl 출시 등 Perl에 관련된 공고가 올라오는, 일반적인(포스팅되는 기사를 관리자가 열람한 후에 적절한 내용만 등록됨) 뉴스그룹이다. 이곳에는 새 버전 출시나 버그 고침, 새로운 확장이나 모듈, 그리고 FAQ(Frequently Asked Question)등이 올라온다.

매일같이 많은 글이 올라오는 comp.lang.perl.misc는 Perl의 기술적인 측면에서부터 Perl의 철학, Perl게임, Perl로 만든 시(poetry)에 이르기까지 온갖 이야기가 올라오는 뉴스그룹이다. Perl과 마찬가지로 comp.lang.perl.misc는 매우 유용한 그룹으로서, Perl에 관한 내용이라면 어떤 것을 물어봐도 괜찮다.

comp.lang.perl.tk 뉴스그룹은 Perl에서 Tk툴킷을 어떻게 사용하는가에 관한 토론을 하는 곳이다. comp.lang.perl.modules 뉴스그룹은 Perl모듈의 개발과 사용에 관한 그룹이며, 재사용 가능한 코드를 얻는 가장 좋은 곳이다. 이 밖에 여러분이 이 책을 읽을 때쯤에는 comp.lang.perl.로 시작하는 또 다른 뉴스그룹이 생겼을 지도 모른다. 살펴보기 바란다.

만약 여러분이 WWW 상에서 CGI 프로그래밍에 관심있다면, comp.infosystems.www.authoring.cgi 뉴스그룹도 빼 놓을 수 없을 것이다. 이 그룹은 엄격히 말해서 Perl에 관한 뉴스그룹은 아니지만, 그곳에 소개된 프로그램의 대부분이 Perl로 작성된 것이다. WWW과 관련된 Perl에 관한 내용은 이 곳에 문의하면 된다.

Perl 홈페이지

여러분이 WWW을 사용할 수 있다면 Perl 홈페이지(http://www.perl.com/perl/)을 둘러보기 바란다. Perl 홈페이지에는 Perl에 관한 새소식, 소스 코드, 타 시스템으로의 이식, 문서, 모듈, Perl 버그 모음 데이터베이스, 메일링 리스트 정보 등 다양한 내용을 찾을 수 있다. 또한 이 사이트에서는 나중에 소개할 CPAN에 대한 내용도 제공하고 있다.

이 밖에 비영리단체인 Perl재단의 홈페이지(http://www.perl.org/)도 둘러보기 바란다.

FAQ 목록(Frequently Asked Questions List)

Perl FAQ는 comp.lang.perl.misc에 자주 등장하는 질문과 답의 모음집이다. 여기에는 사람들이 쉽게 이해하지 못하는 개념에 대한 설명이나, 현재 릴리즈 레벨과 같은 최신 정보, Perl 소스를 구할 수 있는 곳 등과 같은 중요한 내용을 담고 있다.

또한 Perl에 관해 정말 자주 나오는 질문과 답을 모아 놓은 metaFAQ도 있다. 여기에는 현재 Perl 배포본을 구할 수 있는 곳, UNIX가 아닌 다양한 시스템으로의 이식에 관한 내용, 전체 FAQ를 구할 수 있는 곳 등에 대한 정보를 담고 있다. 이 밖에도 WWW 프로그래밍이나 perltk등에 관한 FAQ등 다양한 FAQ가 존재한다.

이 밖에 FAQ와 비슷한 형태의 포스팅으로는 Perl Modules List가 있는데, 여기에는 현존하는 다양한 모듈과, 현재 개발 작업 중이거나 앞으로 개발될 모듈에 관한 내용을 담고 있으며, 개발자의 e-mail주소와 모듈 디자인에서 고려해야 할 것에 관한 조언 등이 포함되어 있다.

이러한 FAQ들은 주기적으로 comp.lang.perl.announce에 올려지며, http://www.perl.com/perl/faq에서 WWW으로도 볼 수 있다.

버그 리포트(Bug Report)

자주 일어나는 일은 아니지만 만약 여러분의 프로그램 내부의 버그가 아니라 Perl 자체의 버그를 발견했을 때는 최소한의 테스트 후에 Perl 패키지에 딸려 오는 perlbug 프로그램을 이용해 보고해야 한다.

Perl 배포

Perl은 다음 둘 중 한 라이센스 하에서 배포된다. 첫 번째는 표준 GNU Copyleft로서, 간략히 말하자면 만약 사용자의 시스템에서 Perl을 실행할 수 있다면 더 이상의 비용 부담없이 Perl의 전체 소스 코드에 접근이 가능해야 한다는 것이다. 다른 하나는 Artistic 라이센스 하에서 배포되는 Perl로서, 앞서 언급한 Copyleft 보다는 좀 덜 위협적인 문구를 담고 있다.

Perl 배포본에는 eg/ 디렉토리 밑에 몇몇 예제 프로그램이 포함되어 있다. 이 외에도 다른 유용한 것들을 찾을 수 있을 것이다. 필요한 경우 이들 예제 프로그램을 참조하기 바란다. 여력이 있으면 Perl 소스 코드를 분석해 보는 것도 좋다. 어떻게 Configure 스크립트가 여러분의 시스템에서 mkdir(2) 시스템 호출의 존재여부를 체크하는지 살펴 보라. Perl이 어떤 방법으로 C 모듈의 동적 로딩(dynamic loading)을 수행하는지 이해해 보라. 그 밖에 무엇이라도 좋다.

참고 서적

Randal Schwartz의 Learning Perl(O’Reilly & Associates 출판)은 이 책의 자매편으로서, 이 책이 참고 도서의 성격이 강한 데 비해 Learning Perl은 입문서이다. Programming Perl의 입문편이 너무 짧거나 여러분이 이해하는 데 부족하다고 생각된다면 Learning Perl을 참고해 보기 바란다.

Aho, Kernighan, Weinberger의 AWK Programming Language (Addison-Wesley 출판)와 Dale Dougherty의 sed&awk는 조합 배열이나 정규 표현식, 그리고 Perl의 기본을 이루고 있는 여러가지 것들에 대한 개념을 이해하는 데 도움이 된다. 여기에는 많은 awk, sed 예제가 포함되어 있으며, 이들 스크립트는 필요한 경우 awk 스크립트를 Perl 스크립트로 변환하는 a2psed 스크립트를 Perl 스크립트로 변환하는 s2p와 같은 프로그램을 사용하여 Perl 스크립트로 변환할 수 있다. Perl의 이러한 변환 프로그램이 Perl의 관용적인 프로그램을 만들어 내지는 않지만, awksed의 예제가 Perl에서 어떻게 구현되는지 잘 이해되지 않을 경우에는 이들 변환 프로그램의 출력물이 좋은 길잡이가 될 것이다.

또한 Johan Vroman의 Perl 5 Desktop Reference(O’Reilly & Associates 출판)를 쉽고 빠르게 찾아볼 수 있는 참고서로 추천한다.

Perl 구하는 방법

Perl의 주 배포처는 CPAN(Comprehensive Perl Archive Network)이다. 이곳에는 Perl 의 소스 코드 뿐 아니라, Perl과 관련된 거의 모든 것을 구할 수 있다. CPAN은 아래 표기한 몇 군데 외에도 전 세계에 걸쳐 수십 곳에서 미러되고 있다. CPAN의 주 사이트는 ftp.funet.fi(128.214.248.6)이다. ftp.funet.fi/pub/languages/perl/CPAN/MIRRORS 파일을 받아 보면 자신과 가까운 CPAN 지역 사이트의 리스트를 알 수 있다. 혹은 웹 브라우저를 사용하여 http://www.perl.com에서 CPAN의 다중 연결 서비스에 접속할 수도 있다. 여러분이 이 웹 서버를 통하여 /CPAN/으로 시작하는 파일을 요청할 경우, 여러분의 도메인 명을 보고 가장 가까운 CPAN사이트로 연결시켜 준다. 다음은 몇몇 유명한 CPAN사이트의 URL(Universal Resource Locator)이다.

http://www.perl.com/CPAN/
http://www.perl.com/CPAN/README.html
http://www.perl.com/CPAN/modules/
http://www.perl.com/CPAN/ports/
http://www.perl.com/CPAN/src/latest.tar.gz

CPAN 다중 연결 서비스는 가능한 한 사용자에게 가깝고 큰 대역폭을 가진 빠른 컴퓨터로 접속하도록 해 준다. 그러나 사용자의 도메인 명이 네트워크 연결 상태와 일치하는 것은 아니기 때문에 항상 성립하는 것은 아니다. 예를 들어 사용자의 호스트 명이 .se로 끝날 경우 실제로는 북미 지역보다 스웨덴 쪽의 서버로 연결되는 것이 더 나을 것이다. 이 경우 다음 URL을 사용해서 자신에게 맞는 사이트를 선택할 수 있다.

http://www.perl.com/CPAN

위의 URL에서 끝에 슬래쉬(/)가 없음을 주의하기 바란다. 위와 같이 끝에 슬래쉬(/)를 생략하면 CPAN 다중 연결 서비스에 의해 여러분이 가장 가까운 사이트를 고를 수 있도록 하는 메뉴를 보여준다. 그 다음부터는 여러분이 선택한 사이트를 기억했다가 다음 번 연결시 사용할 수 있도록 해 준다.

Perl 소스 코드 및 CPAN 미러 사이트 목록은 다음 anonymous FTP에서 구할 수 있다.(가능하면 호스트 명을 이용하는 것이 좋다. IP 주소는 필요에 의해 변경될 수도 있기 때문이다.)

ftp.perl.com 199.45.129.30
ftp.cs.colorado.edu 131.211.80.17
ftp.cise.ufl.edu 128.227.162.34
ftp.funet.fi 128.214.248.6
ftp.cs.ruu.nl 131.211.80.17

CPAN 미러 사이트의 최상위 디렉토리는 서버마다 다를 수 있으니 살펴보기 바란다. 특별한 경우를 제외하고는 대부분 /pub/perl/CPAN과 같이 되어 있다.

무슨 파일이 있나

CPAN 디렉토리 밑에는 다음과 같은 서브 디렉토리가 있다.

authors. 이 디렉토리 밑에는 각 소프트웨어의 저자 별로 수 많은 디렉토리가 존재한다. 예를 들어 Lincoln Stein의 CGI 모듈을 찾고 싶을 경우, 그리고 그 사람이 해당 프로그램을 작성했다는 사실을 알고 있을 경우, authors/Lincoln_Stein을 찾아 보면 될 것이다. 만약 누가 그런 프로그램을 작성했는지 모른다면, 아래 설명할 modules 디렉토리를 찾아 보아야 한다.

doc. Perl의 모든 문서 파일이 들어 있다. 여기에는 여러 가지 문서 포맷(ASCII 텍스트, HTML, PostScript, POD 포맷 등)으로 되어 있는 Perl의 공식 문서 외에 FAQ나 몇몇 보조 문서 들이 포함된다.

ports. Perl의 기본 배포판에서 직접 지원되지 않는 타 운영 체제로 이식된 소스 코드나 바이너리 파일 등이 들어 있다. 이들은 각각의 저자 개인의 노력에 의해 제공되는 것으로서, 이 책에서 설명한 것과 정확히 같게 동작하지 않을 수도 있다. 예를 들어, MS-DOS에서는 몇 가지 이유 때문에 fork함수를 구현하지 못한다.

scripts. 다양한 스크립트의 모음. 만약 프로그램 작성시 구체적으로 어떻게 해야 할 지 모를 때나, 다른 사람들이 Perl 프로그램을 어떻게 작성하는 지 알고 싶을 때에는 이 디렉토리의 내용을 참고하기 바란다. 이 밑의 서브디렉토리 nutshell에는 이 책의 예제 프로그램이 들어 있다.(혹은 ftp.ora.com 사이트의 /pub/examples/nutshell/programming_perl2/에서 찾아 볼 수도 있다.)

src. 표준 Perl 배포판의 소스 코드를 담고 있다. 현재 나와 있는 가장 최신 릴리즈는 src/latest.tar.gz이며, 이 책이 출간되는 시점에서는 src/5.0/perl5.003/tar.gz에 심볼릭 링크 되어 있었으나, 여러분이 이 책을 읽는 시점에서는 더 높은 버전의 파일로 연결되어 있을 것이다. 이 큰 파일에는 Perl의 전체 소스 코드와 문서 파일이 들어 있다. UNIX및 UNIX와 유사한 시스템(VMS, OS/2 등)에서 환경 설정 및 설치하는 것은 그리 어렵지 않다.

Anonymous FTP 사용하기

anonymous FTP를 한 번도 사용해 본 적이 없는 경우라면, 다음 실제로 접속한 예를 참고로 하여 접속해 보기 바란다. 볼드체로 표기된 것은 여러분이 직접 타이핑해야 하는 내용이며, 이탤릭으로 되어 있는 것은 주석문이고, %는 프롬프트로서 여러분이 타이핑해야 하는 글자가 아니다.

% ftp ftp.CPAN.org (ftp.CPAN.org 실제로 없다)

Connected to ftp.CPAN.org.
220 CPAN FTP server (Version wu-2.4(1) Fri Dec 1 00:00:00 EST 1995) ready.
Name (ftp.CPAN.org:CPAN): anonymous
331 Guest login ok, send your complete e-mail address as password.
Password: camel@nutshell.com (여러분의 e-mail주소를 넣는다)
230 Guest login ok, access restrictions apply.
ftp> cd pub/perl/CPAN/src
250 CWD command successful.
ftp> binary (이진파일 전송모드로 변경)
200 Type set to I.
ftp> get latest.tar.gz
200 PORT command successful.
150 Opening BINARY mode data connection for FILE.
226 Transfer complete.
.
. (원하는 파일을 찾을때까지 반복한다)
.
ftp> quit
221 Goodbye.
%

파일을 전송받고 난 다음에는 맨 먼저 zip압축을 풀고 tar를 해제한 다음, 설정(configure), 컴파일, 그리고 설치의 순서대로 작업이 이루어진다.

% gunzip < latest.tar.gz | tar xvf
% cd perl5.003 (실제 디렉토리이름을 사용한다)

다음 두 가지중 한 가지를 선택한다:

% sh configure (소문자 “c” 자동설정)
% sh Configure (대문자 “C” 수동설정)
% make (Perl 컴파일)
% make test (컴파일이 제대로 되었는지 테스트)
% make install (관리자만 할 수 있다)

모듈 갖고 오기

Perl에 기본적으로 포함되어 있지 않은 모듈을 설치하려고 하는 경우에는 위와 같이 Perl을 설치하는 경우와는 조금 다르다. CoolMod라는 모듈을 설치하는 경우를 예를 들어 보도록 하자. ftp(1)나 웹 브라우저를 통해 원하는 모듈을 받아 온다. 브라우저를 이용해 모듈을 받아 오는 경우 URL은 다음과 비슷하게 될 것이다.

http://www.perl.com/cgi-bin/cpan_mod?module=CoolMod

일단 파일을 다운 받은 다음에는 다음과 같이 하면 된다.

% gunzip < CoolMod-2.34.tar.gz | tar xvf –
% cd CoolMod-2.34
% perl Makefile.PL (Creates the real Makefile)
% make (Build the whole module.)
% make test (Make sure it works.)
% make install (Probably should be the superuser)

CoolMod 모듈이 성공적으로 설치된 다음부터(모듈은 자동으로 시스템의 Perl 라이브러리 경로에 설치된다.) use CoolMod와 같이 프로그램 내에서 사용하거나 man CoolMod와 같이 모듈의 문서를 읽을 수 있다. (perldoc CoolMod도 가능하다.)

표기상의 약속

파일의 이름과 UNIX 유틸리티는 이탤릭체로, Perl 함수명이나 연산자, 그리고 키워드는 볼드체로, Perl 예제는 constantwidth로, 사용자가 적절히 바꾸어 입력하여야 하는 내용은 italic constant width로 표기하였다. 데이터 값은 “constant width”와 같이 겹 따옴표 안에 표기하였으며 이 때 겹 따옴표는 값의 일부로 표시한 것이 아님을 주의하기 바란다.

감사의 글

이 포스팅은 많은 분들의 도움이 없이는 만들어지지 못했을 것이다. 모든 분의 이름을 여기에 다 언급할 수는 없겠지만, 다음 분들에게 이 자리를 빌어서나마 공식적으로 감사의 말씀을 전한다: Ilya Zakharevich, Johan Vromans, Mike Stok, Aaron Sherman, David Muir Sharnoff, Gurusamy Sarathy, Tony Sanders, Chip Salzenberg, Dean Roehrich, Randy J. Ray, Jon Orwant, Jeff Okamoto, Bill Middleton, Paul Marquess, John Macdonald, Andreas Koenig, Nick Ing-Simmons, Sharon Hopkins, Jarkko Hietaniemi, Felix Gallo, Hallvard B. Furuseth, Jeffrey Friedl, Chaim Frenkel, Daniel Faigin, Andy Dougherty, Tim Bunce, Mark Biggar, Malcolm Beattie, Graham Barr, Charles Bailey, and Kenneth Albanowski. (이상 무순)

또한 저자는 이 책을 준비하는 오랜 기간 동안 피곤하고 지친 가운데에서도 계속 친구(및 친지)로 남아 준 모든 친구(및 친지)들에게도 감사의 뜻을 전하고 싶다.

저자들로 하여금 사람들이 즐겁게 읽을 수 있는 종류의 책을 쓰도록 용기를 복돋아 주신 Tim O’Reilly에게 특별한 감사의 말씀을 전한다.

또한 O’Reilly & Associates의 스탭들에게도 감사드린다. Technical editor인 Steve Talbott, production editor겸 project manager인 Nicole Gipson Arigo, copyeditor인 Joseph Pomerance, 교정을 봐 주신 Steven Kleinedler에게 감사드린다. Quality control check를 담당하신 Kismet McDonough-Chan과 Sheryl Avruch씨, 인덱스를 작성해 주신 Seth Maislin, 이 책을 만드는 데 필요한 각종 툴을 제공하신 Erik Ray, Ellen Siever, Lenny Muellner, 책 내부 레이아웃을 디자인해 주신 Nancy Priest와 Mary Jane Walsh, 그리고 표지 커버를 디자인하신 Edie Freedman과 Hanna Dyer에게도 감사의 말씀을 드린다.

여러분의 의견을 듣고 싶습니다

우리는 가능한 한 모든 내용에 대해 테스트 및 검증을 하려고 노력했지만 실수가 있을 수 있다. 만약 내용에서 오류를 발견하면 아래 주소로 연락바란다.

O’Reilly & Associates, Inc.
101 Morris Street
Sebastopol, CA 95472
1-800-998-9938 (in the US or Canada)
1-707-829-0515 (international/local)
1-707-829-0104 (FAX)

또한 전자우편을 사용해 연락할 수도 있다. 메일링 리스트에 가입하거나 카타로그를 요청하려면 다음 주소를 이용해 주기 바란다.

nuts@ora.com (via the Internet)
uunet!ora!info (via UUCP)

이 책에 대한 기술적인 질문이나 코멘트는 다음 주소로 보내주기 바란다.

bookquestion@ora.com (via the Internet)

 

시작하기

Perl(펄이라고 읽는다: 역자)은 배우기 쉽고 사용하기 편한 언어다. Perl을 쉬운 언어라고 말하는 이유 중의 하나는 바로 Perl에서는 프로그래머가 표현하고 싶은 것을 서술하기 이전에 많은 것을 선언할 필요가 없다는 점이다.

다른 대부분의 프로그래밍 언어에서는 실제로 실행되는 프로그램의 첫 문장을 기술하기 전에 데이터 타입이나 변수, 서브루틴(subroutine) 등을 미리 선언해야 한다. 이런 점들은 복잡한 데이터 구조를 필요로 하는 어려운 문제들을 해결하는 데에는 도움이 될 지 모른다. 이런 점들은 복잡한 데이터 구조를 필요로 하는 어려운 문제들을 해결하는 데에는 도움이 될 지 모른다. 그러나 매일 부딪히게 되는 간단한 문제들을 해결하는 데에는 다음과 같이 간단히 기술할 수 있는 프로그래밍 언어를 사용하는 것이 훨씬 나을 것이다.

print “Howdy, world!\n”;

Perl이 바로 그러한 언어이다. 실제로 위의 예제는 하나의 완벽한 프로그램이며, 이 예제 프로그램을 Perl 인터프리터로 실행하면 화면에 “Howdy, world!”라는 내용이 출력될 것이다.

또한 Perl에서는 프로그래머가 표현하고 싶은 것을 서술한 다음에 더 많은 것을 기술할 필요는 없다. 대부분의 다른 프로그래밍 언어와 달리, Perl에서는 프로그램의 끝까지 순서대로 실행하는 것이 프로그램의 실행을 마치는 일반적인 방법이다.

물론 프로그래머의 필요에 따라서 원하는 경우에는 exit 함수를 사용하여 명확하게 프로그램의 종료를 나타낼 수도 있고, 실제로 변수나 서브루틴을 사용하기 이전에 미리 명확하게 선언해 둘 수도 있으며, 미리 선언해야만 하도록 지정할 수도 있다. 그러나 이 모든 것은 프로그래머의 선택에 달린 것이다. Perl에서는 사용자의 자유가 최대한 보장되어 있다.

이밖에도 왜 Perl이 배우고 사용하기 쉬운 언어인지에 관한 여러 가지 이유가 있지만 지금 그러한 이유들을 일일이 나열할 필요는 없을 것 같다. 이 책을 읽으면서 자연스럽게 그 이유에 대해 알 수 있기 때문이다. 물론 보다 깊이 들여다 보면 어려운 점이 있을 수 있겠지만, Perl은 그러한 문제들을 최소한의 노력으로 해결할 수 있도록 도와줄 것이다. 이 점이 바로 다른 언어와 달리 Perl 프로그래머가 얼굴에 미소를 잃지 않고 프로그램을 작성할 수 있도록 하는 까닭이다.

이 장에서는 Perl의 개요에 대해 다룬다. 따라서 Perl에 관해 완벽하게, 그리고 논리적으로 다루지는 않을 것이다. 이 장에서는 마치 코끼리 다리를 만지는 시각 장애인에게 Perl에 관한 명확한 그림을 전달한다는 기분으로 Perl의 다양한 면을 보여줄 것이다.

자연어와 인공 언어

언어란 인간이 인간을 위해 만든 것이다. 그러나 오늘날과 같은 컴퓨터 시대에서는 이러한 사실이 때때로 잊혀질 때가 있다. Perl은 마치 자연어로 의사소통을 원활하게 하는 것과 비슷하게 동작하도록 만들어졌다. 언어학적인 측면에서 여러 가지 원리를 열거할 수도 있겠으나, 여기서 말하고 싶은 것은 언어를 디자인할 때 가장 중요한 점은, 쉬운 것은 쉬워야 하면서 동시에 어려운 것도 표현이 가능해야 한다는 것이다. 어떻게 보면 당연하게 보이는 이것이 실제로 많은 컴퓨터 언어에서 지켜지지 않고 있는 것이 오늘의 현실이다.

이러한 점에서 보면 자연어는 훌륭하다고 할 수 있다. 즉, 사람들은 자연어를 이용해 단순한 것과 복잡한 것을 끊임없이 전달하려 하고 있으며, 그 결과 언어는 단순한 것과 복잡한 것을 모두 표현할 수 있도록 진화해 나가고 있다. Perl도 자연 언어와 마찬가지로 진화해 나갈 수 있도록 고안되었으며, 실제로 진화해 가고 있다. 실제로 수 년에 걸쳐서 많은 사람들이 Perl의 진화에 공헌해 왔다.

누군가 “언어학”이라는 단어를 말했을 때 사람들은 단어 혹은 문장, 둘 중의 하나를 생각할 것이다. 그러나 이러한 단어나 문장이라는 것도 결국은 말(다시 말해 언어)을 나누기 위한 방법에 불과하다. 이러한 단어나 문장도 다시 의미를 갖고 있는 더 작은 단위로 쪼개어지든지, 혹은 여러 개가 모여서 의미를 갖는 더 큰 단위를 이루기도 한다.

자연어에서는 명사, 동사 따위의 다양한 단어가 존재한다. 단어 하나를 말했을 때 그것이 명사일 지라도 문장 내부에서의 위치나 쓰임새에 따라서 동사로, 형용사로 또는 부사로도 사용될 수 있는 것이다.

자연어와 마찬가지로 Perl에서도 어떤 단어는 그 단어가 놓여진 위치에 따라서 다양한 방식으로 해석되고 동작한다. 다만 Perl은 프로그래머가 지금 무엇을 표현하고 싶은지를 이해하기 위해 최대한 노력한다는 점을 명심해 주기 바란다. 대부분의 경우 어떻게 표현하더라도 Perl은 이해하고 받아들인다.(물론 말도 안되게 작성한 경우는 제외되겠지만 Perl 해석기(parser)는 영어나 스와힐리어보다는 Perl을 더 잘 이해하기 때문이다)

명사를 생각해 보자. 명사는 특정한 객체(object)의 이름일 수도 있고, 현재 참조되고 있는 사실을 명확하게 보여주지는 않지만 어느 객체 클래스의 이름을 나타낼 수도 있다. 대부분의 컴퓨터 언어에서는 이러한 구분을 뚜렷이 하기 때문에 특정한 무엇을 말할 때는 값(value)이라고 하고, 일반적인 무엇을 말할 때는 변수(variable)라고 한다.

값이란 어떤 곳에 존재하고, 그 값이 어디에 있는지를 알고 있는 것임에 비해, 변수는 그 변수가 존재하는 동안에 하나 혹은 그 이상의 여러 가지 값을 가질 수 있다는 것을 말한다. 따라서 어떤 변수를 해석하기 위해서는 프로그램의 진행에 따른 변수 값의 변화를 추적해야만 한다. 이러한 해석은 여러분의 두뇌에서나 혹은 컴퓨터가 하게 될 것이다.

명사

변수란 어떤 값을 저장할 수 있는 이름을 가지는 장소로, 이러한 변수를 이용하여 특정한 값을 저장한 다음 나중에 참조할 수 있다. 실제로 일상 생활에서도 개인적으로 또는 공공을 위한 목적으로 다양한 종류의 보관 장소들을 찾아볼 수 있다. 이런 장소들은 임시로 사용되기도 하지만 영구적으로 이용될 수 있는 곳도 있다.

전산학에서는 흔히 변수의 범위(scope)에 관해 많이 언급하기도 한다. Perl에서는 이러한 변수의 범위에 관해 다양한 방법으로 접근하고 있으며 이러한 내용은 3장에서 자세히 다룰 것이다. (궁금한 분은 3장에서 “local”과 “my”에 관한 항목을 살펴보기 바란다.)

이러한 변수를 구분하는 데 더 유용한 방법은, 그 변수가 담고 있는 데이터의 종류를 기준으로 구분하는 것이다. 영어에서와 마찬가지로 Perl에서 가장 기본적인 구분은 단수 데이터와 복수 데이터이다. 문자열과 숫자는 단수 데이터에 속하며, 문자열이나 숫자의 리스트(list)는 복수 데이터이다. (객체지향 프로그래밍 부분에 도달하게 되면, 마치 어떤 한 학급의 학생들처럼 특정 객체가 밖에서는 단수 데이터로 보이지만 객체 내부에서는 복수로 보이는 예를 보게 될 것이다.)

이러한 단수 데이터를 스칼라(scalar)라고 부르며, 복수 데이터를 배열(array)이라 한다. 다음 예에서 문자열을 스칼라 변수에 저장하는 것을 살펴보도록 하자.

$phrase = “Howdy, world!\n”; # 변수를 지정.

print $phrase; # 변수의 내용을 출력.

위의 예에서 $phrase라는 변수를 미리 선언하지 않은 점을 유의해서 보아주기 바란다. $는 Perl로 하여금 phrase가 스칼라 변수, 즉 단수 데이터를 포함하고 있는 것을 알게 해 준다. 이에 반해 배열은 @로 시작한다. 다음을 이해하면 기억하기 쉬울 것이다. $는 스칼라(scalar)의 머리 글자 S를, @는 배열(array)의 머리 글자 a를 나타낸 것이다.

이 밖에도 Perl에는 해쉬(hash)나 핸들(handle), 타입글로브(typeglob) 등과 같은 데이터 타입이 존재하며, 스칼라나 배열과 마찬가지로 특정한 머리 글자를 통해 구분하게 된다. 다음 표에 데이터 타입을 구분하는 머리 글자를 정리해 두었다.
[표 1-1] 변수 표기법

타입 머리글자 설명
스칼라 $ $cents 특정한 값(숫자나 문자열)
배열 @ @large 숫자 키를 가진 값의 리스트
해쉬 % %interest 문자열 키를 가진 값의 리스트
서브루틴 & &how Perl의 실행 가능한 코드 일부분
Typeglob * *struck struck이라고 이름 붙은 모든것
 
단수성(Singularities)

앞의 예에서 보았듯이 Perl에서도 다른 대부분의 컴퓨터 언어에서와 마찬가지로 = 연산자를 이용해 스칼라 변수에 새로운 값을 대입한다. 스칼라 변수에는 정수, 실수, 문자열, 그리고 다른 변수나 객체를 가리키는 레퍼런스 등 다양한 형태의 값을 대입할 수 있다.

Perl에서는 유닉스(Unix) 쉘에서와 마찬가지로 다양한 방식으로 인용부호를 사용하여 여러 가지 타입의 값을 표현할 수 있다. 겹 따옴표(“) 안에서는 변수 삽입(interpolation)과 역슬래쉬(\) 표현이 가능하고, 인용부호(‘) 안에서는 이들 모두의 변환이 이루어지지 않는다. 역 인용부호(`)는 따옴표로 둘러싸여 있는 외부 명령(혹은 프로그램)을 실행하도록 한 다음 그 반환값을 받을 수 있게 한다. 따라서 외부 명령의 실행 결과를 하나의 문자열로 받을 수 있다.

$answer = 42; # 정수
$pi = 3.14159265; # 실수
$avocados = 6.02e23; # 지수 표현
$pet = “Camel”; # 문자열
$sign = “I love my $pet”; # 변수 삽입된 문자열
$cost = ‘It costs $100’; # 변수 삽입되지 않은 문자열
$thence = $whence; # 다른 변수의 대입
$x = $moles * $avocados; # 표현식
$cwd = `pwd`; # 명령어 실행 후 그 결과를 문자열로 저장
$exit = system(“vi $x”); # 명령어(system) 의 반환값을 저장
$fido = new Camel “Fido”; # 객체

특정한 값으로 초기화되지 않은 변수는 처음에 널(Null) 값(“” 혹은 0)으로 지정되었다가, 나중에 사용되었을 때 해당 변수가 사용되는 위치나 대입되는 값에 따라서 문자열이나 숫자, 혹은 참, 거짓을 나타내는 불린(Boolean) 등과 같은 타입이 자동으로 결정된다. Perl에는 다양한 연산자가 존재하며 이들 연산자는 각각의 종류에 따라 특정한 형태의 값을 파라미터(매개변수)로 요구한다. Perl에서는 현재의 구문(context)에 맞도록 자동으로 데이터를 필요한 형태로 변환하는데, 아래의 예에서 살펴보기로 하자.

$camels = ‘123’;

print $camels + 1, “\n”;

위에서 첫째 줄에 의해 $camels는 문자열 변수로 자동 지정된다. 문자열 값인 ‘123’이 대입되었으므로. 둘째 줄에서 $camels는 숫자 1을 더하기 위해 자동으로 문자열에서 숫자 123으로 변환되어 124(123+1)의 숫자 값을 가진 후, 다시 문자열 ‘124’로 변환되어 화면에 출력된다. 줄바꿈을 나타내는 “\n”은 애초에 문자열이었기 때문에 출력하기 위해 더 이상의 변환은 이루어지지 않는다.

여기서 주의할 점은 줄바꿈 기호를 나타낼 때 겹 따옴표를 사용하였다는 것이다. 만약 홑 따옴표를 사용하여 ‘\n’과 같이 나타낼 경우, 이것은 줄바꿈을 나타내는 것이 아니라 단순히 “\“와 “n“ 두 글자로 이루어진 문자열을 표현하는 것이다. 겹 따옴표와 홑 따옴표는 특정한 형태의 구문을 나타내기 위해 사용하며, 따옴표 안에서의 삽입, 치환은 둘 중에 어느 것을 사용했는지에 따라 다르게 나타난다.

복수성(Pluralities)

여러 개의 값을 한데 묶어서 한 번에 그 값을 저장할 필요가 있을 때가 있다. Perl에서는 이런 경우 두 가지 형태의 변수 타입을 제공한다. 배열(array)과 해쉬(hash)가 바로 그것이다. 많은 경우 이들은 스칼라와 비슷하게 취급된다. 배열이나 해쉬 변수에 어떤 값들을 대입하려면 대입식의 우측에 리스트 구문(list context)에 해당하는 것을 적어 주어야 한다.

어떤 값들을 숫자 인덱스에 의해 구분하고 싶을 경우에는 배열을 사용하고, 이름 인덱스에 의해 구분할 경우에는 해쉬를 사용한다. 이들 타입은 서로 상호 보완적이다.

배열 배열은 스칼라 값을 순서대로 나열한 리스트로서 리스트 내의 순서(index)에 따라 그 값을 참조할 수 있다. 배열의 요소로는 숫자나 문자열, 혹은 이 두 가지가 섞인 형태의 값이 올 수 있다. 사실 이들 외에도 배열의 요소에는 다른 리스트의 레퍼런스가 올 수도 있다. 이런 경우에 대해서는 4장에서 자세히 살펴보기로 하자. 배열에 리스트 값을 대입하려고 할 때에는 괄호를 사용하여 다음과 같이 나타낸다.

@home = (“couch”, “chair”, “table”, “stove”);

역으로 @home을 대입식의 우변에 위치하는 경우와 같이 리스트 구문에서 사용할 경우, 다음과 같이 할 수도 있다. 이 때 좌변에는 4개의 스칼라 값을 갖는 변수를 지정하면 된다.

($potato, $lift, $tennis, $pipe) = @home;

이것을 리스트 대입(list assignment)이라고 한다. 이러한 대입은 병렬로 진행되므로 다음과 같이 두 변수 값의 교환이 자연스럽게 이루어진다.

($alpha,$omega) = ($omega,$alpha);

C에서와 마찬가지로, 배열의 인덱스는 0에서부터 시작한다. 따라서 4개의 구성 요소를 갖고 있는 배열에서는 인덱스의 값이 0, 1, 2, 3이 된다. 배열에서 인덱스는 [ ] 기호 안에 위치하며, 배열의 특정한 요소를 참조하고 싶을 경우에는 $home[n]과 같이 배열의 이름과 인덱스를 동시에 지정해야 한다. 아래의 예를 참고하기 바란다. 배열의 구성 요소 자체는 스칼라 값이므로 이 경우 특정 요소를 나타낼 때 배열 이름 앞에 반드시 $ 를 붙여야 한다.

배열에 값을 대입할 때, 위의 예처럼 한꺼번에 하지 않고 한 번에 하나씩 대입하려면 다음과 같이 한다.

$home[0] = “couch”;
$home[1] = “chair”;
$home[2] = “table”;
$home[3] = “stove”;

배열은 그 요소의 순서가 정해져 있는 상태이므로, push, pop과 같은 스택 연산 등 다양한 연산자를 사용할 수 있다. 스택은 결국 입/출력 순서가 정해져 있는 리스트라고 할 수 있다. Perl에서는 리스트의 끝을 스택의 최상단(top)으로 간주한다.

해쉬 해쉬는 배열과는 달리 숫자로 된 인덱스가 아닌 임의의 스칼라 인덱스에 의해 참조되는, 순서가 정해져 있지 않은 스칼라 리스트이다. 이러한 이유로 흔히 해쉬를 일컬어 조합 배열(associative array)이라 부르기도 한다. 이것을 해쉬라고 이름 붙인 이유 중 하나로, 배열과 달리 요소의 순서가 정해져 있지 않다는 것이다.(Perl 내부에서 해쉬를 구현하기 위해서는 해쉬 표 참조(hash table lookup)라는 방법을 사용하고 있다.) 요소의 순서가 정해져 있지 않으므로, 앞서 말한 배열의 경우와 달리 해쉬에서는 push나 pop과 같은 스택 연산자를 사용할 수 없다. 해쉬에는 시작이나 끝을 알리는 값이 없다. 그럼에도 불구하고 해쉬는 매우 중요하며 상당히 유용한 것임에 틀림없다.

해쉬의 구성 요소를 참조할 때 사용되는 키(key) 값은 배열과 달리 그 구성 요소의 위치에 따라 자동으로 정해지는 것이 아니므로, 처음 해쉬를 지정할 때 해쉬 구성 요소의 값과 키 값을 같이 적어 주어야 한다. 물론 해쉬를 배열과 같은 방법으로 초기값을 지정할 수도 있지만, 그 값들이 모두 해쉬의 구성요소 값으로 들어가는 것이 아니라, 앞에서부터 순서대로 키/요소 값의 쌍으로 대입된다는 것을 명심하여야 할 것이다. 만약 3글자로 축약된 형태의 요일 명을 원래 요일 명으로 변환하도록 하기 위한 해쉬를 만드는 경우 아래 예와 같이 하면 된다.

%longday = (“Sun”, “Sunday”, “Mon”, “Monday”, “Tue”, “Tuesday”, “Wed”, “Wednesday”, “Thu”, “Thursday”, “Fri”, “Friday”, “Sat”, “Saturday”);

그러나 위와 같이 할 경우 키 값과 요소 값의 구분이 한 눈에 들어오지 않으므로, Perl 에서는 해쉬를 정의할 때 => 연산자를 이용하여 키와 그 값을 한 쌍으로 묶어 지정할 수 있도록 하고 있다.

%longday = ( “Sun” => “Sunday”, “Mon” => “Monday”, “Tue” => “Tuesday”, “Wed” => “Wednesday”, “Thu” => “Thursday”, “Fri” => “Friday”, “Sat” => “Saturday”, );

앞서 배열에서와 마찬가지로, 해쉬 변수에 키/값의 리스트를 대입하는 것과 반대 방향으로 대입할 수도 있다. 해쉬는 keys 함수를 사용하여 해쉬의 키만 뽑아낼 수도 있으며, 뽑아낸 키를 sort 함수를 이용해 원하는 순서대로 나열할 수도 있다. 자세한 설명은 나중에 하기로 하자.

해쉬의 특정 요소를 참조하려면 해쉬의 키를 { }를 사용하여 배열의 인덱스처럼 참조하면 된다. 위의 예에서 Wed키에 해당하는 값을 참조하기 위해서는 $longday{Wed} 와 같이 하면 된다. 앞서 배열에서와 마찬가지로 해쉬의 특정 구성 요소의 값은 스칼라이므로 %가 아니라 $를 앞에 붙여야 한다는 것을 주의하여야 한다.

동사

일반적인 다른 컴퓨터 언어와 마찬가지로 Perl에서도 대부분의 동사에 해당하는 것은 명령어이다. 이들 명령어는 Perl로 하여금 특정한 무언가를 하도록 지시한다. 자연어에서 동사로 시작하는 문장이 명령문인 것과 마찬가지로 Perl에서도 동사로 시작되는 문장은 특정한 동작을 하도록 지시하는 문장이다. 가장 많이 볼 수 있는 예는 아래와 같은 print 명령어일 것이다.

print “Adam’s wife is “, $wife{‘Adam’}, “.\n”;

위와 같이 특정한 작업을 지시하는 문장 외에도 Perl에는 다른 형태의 동사가 존재하며, 이들은 조건문에서 사용되거나 입력된 값을 다른 형태의 출력 값으로 변환하는 등의 일을 한다. Perl에서는 이러한 동사를 함수(function)라고 부른다.

Perl에서 미리 정의된(built-in) 함수의 예 중 하나는 다음과 같은 지수 함수이다.

$e = exp(1); # 2.718281828459

Perl에서는 프로시져(procedure)와 함수를 뚜렷이 구분하고 있지는 않다. 필요에 따라서 이 두 가지 용어를 번갈아가면서 사용할 것이다. 또한 서브루틴이나 연산자(operator)등도 Perl에서의 동사에 속하는 것들이다. 이들을 무엇이라고 불러도 좋다. 이들은 모두 그 값이 의미가 있든지 없든지 어떤 값을 반환하며, 반환된 값을 사용하든 안하든 그것은 전적으로 프로그래머의 자유다.

앞으로 이 책에서는 Perl과 자연어의 닮은 점을 몇 가지 더 보게 될 것이다. 물론 자연어와 비교하는 것 외에도 Perl을 보는 또 다른 방법이 얼마든지 있을 수 있다. 더하기, 빼기, 지수함수와 같은 수학적인 문제 외에도, 제어나 프로토타입(prototype)을 만드는데, 텍스트나 리스트를 처리하는 언어로, 그리고 객체지향 언어 등 얼마든지 다른 쪽으로도 Perl을 사용할 수 있다.

그러나 Perl은 동시에 평범한 컴퓨터 언어의 하나이다. 앞으로 볼 예가 바로 그러한 것에 관한 내용이다.

성적처리 예제

한 학급의 성적을 처리하는 경우를 생각해 보도록 하자. 이 때 각 학생별 점수를 기록한 텍스트 파일을 grade라고 하고, 다음과 같은 형식으로 이루어져 있다고 하자.

Noel 25
Ben 76
Clementine 49
Norm 66
Chris 92
Doug 42
Carol 25
Ben 12
Clementine 0
Norm 66

이 경우 아래 스크립트를 이용하여 전체 학생의 총점, 각 학생의 평균을 구한 다음 학생 이름을 알파벳 순으로 출력할 수 있다. 이 프로그램에서는 같은 반에 동명이인이 없다고 가정한다. 즉, 같은 이름이 두 번 나오면 그것은 그 학생의 또 다른 점수라고 가정한다는 것이다.

아래 그림에서 BASIC 언어와 같은 행 번호는 편의상 붙인 것으로서 프로그램의 일부를 말하는 것은 아니다.

1 #!/usr/bin/perl
2
3 open(GRADES, “grades”) or die “Can’t open grades: $!\n”;
4 while ($line = <GRADES>) {
5     ($student, $grade) = split(” “, $line);
6     $grades{$student} .= $grade . ” “;
7 }
8
9 foreach $student (sort keys %grades) {
10     $scores = 0;
11     $total = 0;
12     @grades = split(” “, $grades{$student});
13     foreach $grade (@grades) {
14         $total += $grade;
15         $scores++;
16     }
17     $average = $total / $scores;
18     print “$student: $grades{$student}\tAverage: $average\n”;
19 }

Perl에 대해 계속 알아보기 전에, 위의 예제 프로그램에서 나온 몇 가지에 대해 좀 더 자세히 알아보기로 하자. 만약 다른 주제에 관해 다루기를 원하는 사람은 다른 장으로 옮겨 가도 좋다.

어떻게 실행되나

여러분 중에는 Perl 프로그램을 어떻게 실행시켜야 할 지 모르는 사람도 있을 것이다. 그 방법은 다음과 같다. 즉, 여러분이 작성한 Perl 프로그램(혹은 스크립트)를 perl 인터프리터(대소문자 구분에 주의할 것)에 전달하여 실행하도록 하는 것이다. Perl의 슬로건은 다음과 같다.

어떤 일을 하는 데에는 여러 가지 방법이 있다. (There’s More Than One Way To Do It)

Perl 인터프리터인 perl을 실행하는 방법 중 첫 번째 방법은 명령어 행에서 단순히 perl을 실행하는 것이다. 만약 UNIX 사용자인 경우 -e 스위치를 사용하여 다음과 같이 실행하면 된다.(아래 예에서 % 표시는 쉘 프롬프트를 나타낸 것으로 타이핑할 필요는 없다.)

% perl -e ‘print “Hello, world!\n”;’

UNIX 외의 다른 운영체제에서는 정확하게 위 문장과 같지 않을 수도 있다. 그러나 기본적인 원리는 모두 같다.

위의 예와 같은 한 줄짜리 간단한 것 보다 긴 스크립트의 경우, 일반적인 문서 편집기로 작성한 다음 파일로 저장하고 다음과 같이 실행하면 된다. (스크립트 파일의 이름이 gradation인 경우)

% perl gradation

물론 여전히 perl 인터프리터를 직접 호출하기는 했지만, 이 경우는 위의 예와는 달리 명령어 행에 Perl 문장을 적지 않고 프로그램을 실행할 수 있을 뿐 아니라, 특정 쉘(csh, ksh 등)에서 필요한 인용 부호 등에 관한 문제도 없게 된다.

스크립트를 실행하는 가장 편리한 방법은 스크립트를 직접 실행하고, 운영체제로 하여금 해당 스크립트에 필요한 인터프리터를 찾도록 하는 것이다. 몇몇 운영체제에서는 이러한 일련의 일을 파일의 확장자와 특정 응용 프로그램과의 연결, 혹은 특정 디렉토리와 응용 프로그램의 연결로 해결하기도 한다. 그러한 경우에는 사용자가 Perl 스크립트와 인터프리터를 연결해 주어야만 한다. UNIX 시스템에서는 스크립트의 첫 줄에 #!을 사용하여 해당 스크립트의 실행에 필요한 프로그램을 지정할 수 있도록 되어 있다. 앞서 성적 예제 프로그램의 첫 줄에서

% #!/usr/bin/perl

이라고 적어 놓았을 경우 스크립트를 실행하기 위해서는 단지

% gradation

이라고만 하면 된다. 물론 이 때 스크립트가 실행 가능한 모드(chmod(1) 참조)로 되어 있으며 PATH 안에 포함되어 있는지 확인하여야 한다. 만약 스크립트가 위치하고 있는 디렉토리가 PATH 환경 변수에 포함되어 있지 않다면 다음과 같이 디렉토리를 포함한 스크립트 명을 주어서 실행하도록 해야 한다.

% ../bin/gradation

만약 #! 표기를 지원하지 않는 오래된 UNIX 시스템이거나, perl 인터프리터를 나타내는 절대 경로가 32글자(많은 시스템에서 제한된 한계값)를 넘어서는 경우, 첫 번째 줄을 다음과 같이 하여 해결할 수 있다.

#!/bin/sh — # perl, to stop looping

eval ‘exec /usr/bin/perl -S $0 ${1+”$@”}’

if 0;

몇몇 운영체제에서는 /bin/csh이나 DCL, COMMAND.COM 등과 같은 기본 명령어 해석기에 따라 적절한 변형을 하여야 할 수 있을 것이다. 자세한 것은 주위의 전문가에게 물어보기 바란다.

이 책 전체에서는 앞으로 Perl 인터프리터의 위치를 #!/usr/bin/perl로 나타낼 것이다. 여러분은 각자의 시스템에 맞도록 적절히 변경하기 바란다.

주의사항 1: 만약 시험용 스크립트를 만들 경우, test라는 이름으로 만들지 말기 바란다. UNIX 시스템에는 미리 지정된 test라는 명령어가 있으며 스크립트를 실행할 경우 이 명령어가 실행될 수도 있다. test 대신 try를 사용하라.

주의사항 2: Perl을 처음에 공부할 때, 그리고 나중에라도 개발 중일 때는 perl을 실행할 때 -w 옵션을 사용하길 바란다. 이 옵션을 사용하면 스크립트 실행시 다양하고 유익한 각종 경고 메시지를 출력한다. 스크립트에서는 첫 줄에 다음과 같이 적으면 된다.

#!/usr/bin/perl -w

이제 여러분은 여러분 자신이 만든 Perl 프로그램(perl 인터프리터와 혼동하지 말 것) 을 어떻게 실행하는 지에 관해 배웠다. 그럼 앞서의 예제 프로그램으로 돌아가 보자.

파일 핸들

프로그램 내에서 외부와 어떤 데이터를 주고 받기 위해서는 몇 가지 방법이 있다. 그 중 하나가 앞서 예제 프로그램 3, 4번째 줄에 있는 GRADES와 같이 파일 핸들 이라는 Perl의 데이터 타입이다. 파일 핸들은 특정 파일이나 장치, 소켓, 혹은 파이프의 이름을 알기 쉽고 나중에 참조하기 쉽도록 이름 붙인 것으로서, 이러한 파일 핸들을 사용하게 되면 OS나 인터프리터 내부에서 벌어지는 버퍼링(buffering)등 복잡한 내용을 알고 있지 않아도 된다.(여기서의 파일 핸들은 C++에서의 스트림(stream)이나 BASIC의 I/O 채널과 비슷하다)

파일 핸들은 사용자로 하여금 다양한 곳으로 입/출력을 쉽게 할 수 있도록 해 준다. Perl을 glue 언어의 좋은 예로 드는 이유 중의 하나는 많은 파일이나 프로세스와의 연결을 이러한 파일 핸들을 통해 쉽게 할 수 있다는 데에 있다. 프로그램 외부의 다양한 객체에 잘 어울리는 이름을 붙여 사용할 수 있다는 것이 바로 좋은 glue 언어의 예라고 할 수 있다.

이러한 파일 핸들은 open 함수를 사용하여 생성, 파일에 쉽게 연결할 수 있다. open 함수는 두 개의 파라미터를 갖는데, 하나는 파일 핸들이고 다른 하나는 파일 핸들과 연결하려는 파일명이다. 이 밖에 Perl에는 미리 정의된(그리고 미리 열려 있는) 몇 개의 파일 핸들이 있다. STDIN은 프로그램의 기본 입력 채널, STDOUT은 기본 출력 채널, 그리고 STDERR은 기본 오류 출력 채널을 뜻한다.

open 함수를 사용하여 파일 핸들을 만들 때에는, 다양한 사용 용도(입력, 출력, 파이프)에 따라 다음과 같이 하면 된다.

open(SESAME, “filename”); # 현재 파일로부터 읽기
open(SESAME, “<filename”); # 위와 같음 (읽기를 명확하게 표시)
open(SESAME, “>filename”); # 파일을 만들고 쓰기
open(SESAME, “>>filename”); # 현재 파일 뒤에 덧붙여 쓰기
open(SESAME, “| output-pipe-command”); # 출력 필터 만들기
open(SESAME, “input-pipe-command |”); # 입력 필터 만들기

물론 파일 핸들의 이름은 사용자의 필요에 따라 임의로 지정할 수 있다. open 함수를 통해 일단 열려진 후에는, SESAME라는 파일 핸들은 close(SESAME) 함수를 통해 닫혀지거나 같은 파일 핸들 명으로 다른 파일에 연결되기 전까지[7] 해당 파일이나 파이프를 참조하는 데 사용된다.

입력을 위해 파일 핸들을 만든 후에는(혹은 STDIN을 사용하는 경우), 줄 입력 연산자 < >을 사용하여 한 줄씩 읽어 들일 수 있다. 앵글 연산자 < >을 읽어 들일 파일 핸들 앞 뒤에 붙이면 된다.(예: <SESAME>) 따라서 STDIN 파일 핸들을 사용해 사용자로부터 입력을 받아 들이는 프로그램은 다음과 같이 될 것이다.

print STDOUT “Enter a number: “; # 숫자 입력을 요구

$number = <STDIN>; # 숫자 입력을 받음

print STDOUT “The number is $number\n”; # 숫자를 출력

위의 예에서는 STDOUT 표준 출력을 통해 출력 파일 핸들을 사용하는 예를 보였다. print 함수의 첫 번째 파라미터로 파일 핸들이 올 수 있으며, 이 경우 print 함수의 출력은 해당 파일 핸들로 가게 된다. 위의 경우, 굳이 STDOUT을 지정하지 않더라도 표준 출력으로 print 함수 출력이 나가게 된다. STDIN이 기본 입력인 것과 같이 STDOUT 은 기본 출력에 해당한다.(앞서 성적 프로그램 예제의 18번째 줄에서는 이 같은 이유로 굳이 STDOUT을 지정하지 않았다.)

위의 예제에서 언급하지 않은 것이 또 하나 있다. 예제를 실행하게 되면 아마 빈 줄이 중간에 한 줄 들어가게 될 것이다. 이것은 표준 입력으로부터 값을 읽어 들일 때 자동으로 개행문자가 제거되지 않았기 때문이다.(예를 들어 입력이 “9\n” 과 같은 경우) 이런 경우 개행문자를 제거하려면 Perl에서 기본적으로 제공하는 chop이나 chomp함수를 사용하면 된다. chop 함수는 무조건 입력의 마지막 글자를 제거하는 데 비해, chomp는 레코드 종결 표시자(일반적으로 “\n” )로 지정된 글자를 제거한 다음 제거된 글자의 총 개수를 반환한다. 앞으로의 예제에서는 입력을 받아 들이는 다음과 같은 줄을 자주 보게 될 것이다.

chop($number = <STDIN>); # 숫자 입력 후 줄바꿈 제거

위의 문장은 다음 두 문장을 합해 놓은 것과 같다.

$number = <STDIN>; # 숫자 입력

chop($number); # 줄바꿈 제거

연산자

앞서 말한 대로, Perl은 또한 수학적인 언어이다. 이는 저수준(low level)의 비트 연산에서부터 숫자 및 집합 연산, 그리고 다양한 형태의 추상적 연산에 이르는 다양한 단계의 연산을 행할 수 있다는 것을 의미한다. 수학자들은 그들 나름대로의 각종 수학적 표기법을 만들어내어 사용하듯이, 전산학자도 이러한 수학적인 표기법을 만들어 내곤 한다. Perl에는 C나 FORTRAN, sed(1), awk(1) 등에서 빌려 온 다양한 연산자가 존재하며, 이들에 익숙한 사람들은 그리 낯설지 않을 것이다.

Perl에 기본적으로 포함되어 있는 다양한 연산자는 인수의 개수에 따라 1원(unary), 2원(binary), 3원(ternary) 연산자로 분류된다. 또한 이들 연산자는 그 위치에 따라 삽입(infix) 연산자 및 접두(prefix) 연산자로 분류되기도 하며, 연산이 행해지는 객체의 종류에 따라 분류되기도 한다. 나중에 이들 연산자 모두를 분류한 표를 보기 전에, 우선 몇 가지 기본적인 연산자에 대해 알아보자.

산술 연산자

산술 연산자는 더하기, 빼기 등 기본적인 산술연산을 행하는 연산자이다. 이들은 숫자에 대해 몇 가지 종류의 수학적인 기능을 행한다.

[표 1-2] 몇가지 2원 산술연산자

이름 결과
$a + $b 더하기 $a와 $b의 합
$a * $b 곱하기 $a와 $b의 곱
$a % $b 계수(modulus) $a를 $b로 나눈 나머지
$a ** $b 누승(exponentiation) $a의 $b승
위에서 빼기(-)와 나누기(/) 연산자를 빠뜨렸지만, 이들이 어떻게 동작하는 지에 대해서는 위에 나타낸 연산자와 마찬가지로 여러분이 상상하는 그대로이다. 이들 산술 연산자는 수학 시간에 배운 대로 우선순위에 따라 연산이 행해지므로, 먼저 실행해야 할 연산에 대해서는 괄호를 사용하여 묶어 주어야 한다.

문자열 연산자

Perl의 문자열 연산자에도 “더하기” 연산자가 있는데, 두 개의 문자열을 잇는 문자열 연결 연산자(.)가 바로 그것이다. 다음 예를 보자.

$a = 123;
$b = 456;
print $a + $b; # 579 출력
print $a . $b; # 123456 출력

또한 문자열의 “곱하기” 연산자도 있는데, 이것을 반복(repeat) 연산자라고도 하며 산술 연산자의 곱하기와 구별하기 위해 “x”를 사용한다.

$a = 123;
$b = 3;
print $a * $b; # 369 출력
print $a x $b; # 123123123 출력

이러한 문자열 연산자는 비슷한 기능을 하는 산술 연산자와 같이 긴밀하게 연관되어 있다. 반복 연산자의 경우 왼쪽에는 문자열을, 오른쪽에는 숫자 파라미터를 갖는 것이 조금 특이하다. 또한 위의 예에서 Perl이 어떤 경우에 숫자를 문자열로 자동 변환하는지 주목하기 바란다.

또 한가지 짚고 넘어갈 것이 있다. 문자열 연결은 겹 따옴표 인용부호를 사용한 문자열들을 삽입하는 경우에 묵시적으로 적용된다. 만약 어떤 값들의 리스트를 출력하는 경우 굳이 문자열 연결 연산자를 사용하지 않더라도 해당 문자열이 연결되어 출력된다. 다음 세 가지 예는 모두 같은 출력을 갖는다.

print $a . ‘ is equal to ‘ . $b . “\n”; # 연결 연산자
print $a, ‘ is equal to ‘, $b, “\n”; # 리스트
print “$a is equal to $b\n”; # 삽입 치환

어느 경우에 어떤 방법을 사용할 지는 전적으로 프로그래머에게 달렸다.

아마 처음 보았을 때 “x” 연산자의 필요성을 그리 크게 느끼지 못할 수도 있을 것이다. 그러나 때때로 다음과 같이 아주 유용하게 쓰일 때가 있다.

print “-” x $scrwid, “\n”;

위의 경우 화면의 폭($scrwid) 만큼 가로줄을 긋게 된다.(별도의 for 루프가 필요없다.)

대입 연산자

입 연산자는 이미 앞의 예에서 많이 보았을 것이다. =은 수학에서의 ‘같음’을 나타내는 것이 아니라 오른쪽 값을 왼쪽에 대입한다는 것을 명심해 주기 바란다.(수학에서의 ‘같음’을 나타내는 같음 연산자는 ==이다.)

앞서의 연산자와 마찬가지로 대입 연산자 또한 2진 삽입 연산자로서 연산자 좌우에 피연산자를 갖는다. 연산자의 오른쪽에는 임의의 값이나 수식이 올 수 있지만, 왼쪽에는 반드시 lvalue(변수나 배열의 유효한 저장 위치)라고 하는 피연산자가 와야 한다. 가장 흔한 대입 연산자의 사용은 단순한 대입으로서, 연산자 우측의 결과값을 좌측의 변수에 넣는 것이다.

$a = $b;

$a = $b + 5;

$a = $a * 3;

마지막 줄에서 같은 변수가 두 번 참조된 것을 주목하기 바란다. 한 번은 연산을 위해, 그리고 한 번은 대입할 때 사용되었으며 이것은 잘못 사용된 것이 아니다. C언어에서와 마찬가지로 이러한 경우 축약된 형태의 표기가 가능하다. 만약 다음과 같이 할 경우,

lvalue operator= expression # $var += 1

이것은 다음과 같이 해석되어 실행된다.

lvalue = lvalue operator expression # $var = $var + 1

따라서 위의 경우 다음과 같이

$a *= 3;

“$a에 3을 곱한다”로 되는 것이다. 마찬가지로 Perl에 있는 거의 대부분의 2원 연산자에는 같은 방식이 적용된다.(C언어에서조차 없는 경우도 있다.)

$line .= “\n”; # $line 끝에 개행문자를 추가.

$fill x= 80; # 문자열 $fill을 자신의 80회 반복으로 만들기.

$val ||= “2”; # $val의 값이 지정되지 않았다면 2로 초기화.

앞서 성적처리 예제 프로그램의 6번째 줄에서 두 개의 문자열 연결 연산자를 볼 수 있으며, 이 중 하나는 대입 연산자이다. 그리고 14번째 줄에서는 +=연산자를 볼 수 있다.

어떤 대입 연산자를 사용하든지, 대입 연산의 반환값은 대입한 후의 최종 값이 된다. (Pascal과 같은 언어에서는 대입 연산 문장은 반환값이 없다.) 따라서 앞서 다음과 같은 예에서

chop($number = <STDIN>)

chop 함수는 $number의 최종 값을 파라미터로 갖는다. 이러한 대입 연산은 앞으로 성적 처리 예제 프로그램 4번째 줄과 같은 while 루프에서 자주 보게 될 것이다.

자동 증가 / 자동 감소 연산자

‘$var += 1’과 같은 문장을 더욱 간단하게 하기 위해 Perl은 C언어에서와 같이 변수 값을 증가하거나 감소하기 위해 자동 증가 및 자동 감소 연산자를 제공하고 있다. 이들 자동 증감 연산자는 변수의 앞 뒤 어느 쪽에도 적용될 수 있으며, 언제 변수의 값을 변화시키는가에 따라 다르게 사용된다.

(표 1-3) 1원 산술 연산자

이름 결과
++$a, $a++ 자동 증가 $a에 1을 더하기
–$a, $a– 자동 감소 $a에서 1을 빼기
 

자동 증감 연산자를 변수의 앞에 위치할 경우, 사전 증가(혹은 사전 감소) 변수라고 하며, 해당 변수 값이 참조되기 전에 미리 변수의 값을 증감한다. 변수의 뒤에 연산자가 위치할 경우 사후 증가(혹은 사후 감소) 변수라고 하며 변수의 값이 참조되고 난 다음에 변수의 값을 증감하게 된다. 다음 예를 보자.

$a = 5; #$a에 5를 대입한다
$b = ++$a; #$b는 $a를 증가한 다음 값을 대입하게 되므로 6이 된다
$c = $a–; #$c는 6, 그리고 $a는 감소되어 5가 된다

앞서 성적 처리 예제 프로그램의 15번째 줄에서 성적 하나마다 $scores변수를 자동 증가하도록 하여 나중에 전체 평균을 구할 때 사용하도록 했었다. 이때는 사후 증가 연산자($scores++)를 사용하였으나, 이 경우 lvalue가 없는(즉 lvalue가 피연산자 자신인 경우) 경우이므로 사전 증가 연산자를 사용해도 같은 결과를 갖게 된다. 이 때 연산의 반환값은 사용되지 않고 그냥 버려진다.

논리 연산자

논리 연산자는 프로그램 내에서 중첩 조건문을 사용하지 않고 다양한 기준으로 결정을 내리는 경우에 사용하는 연산자이다. 논리 연산자를 소위 “단락 판별” 연산자라고도 하는데, 이는 연산자 왼쪽의 인수의 값을 판별하는 것으로도 연산의 결과를 낼 수 있는 경우 오른쪽 인수의 결과값 판별을 건너 뛰는 성질을 갖고 있기 때문이다.

Perl에는 실제로 두 세트의 논리 연산자가 있다. 하나는 C언어에서 빌려 온 것들이고, 다른 하나는 알아보기 편한 영어 단어로 된 것들이며, 이들 두 가지는 동일한 결과를 낳는다.

[표 1-4] 논리 연산자

이름 결과
$a && $b and $a가 거짓이면 $a, 그렇지 않으면 $b
$a || $b or $a가 참이면 $a, 그렇지 않으면 $b
!$a not $a가 참이 아니면 참
$a and $b and $a가 거짓이면 $a, 그렇지 않으면 $b
$a or $b or $a가 참이면 $a, 그렇지 않으면 $b
not $a not $a가 참이 아니면 참
이러한 논리 연산자의 “단락 판별” 성질을 이용하여, 조건 실행문을 만드는 데에 이들 논리 연산자가 종종 사용되곤 한다. 앞서 성적 처리 예제에서와 같이 grades라는 파일을 여는 경우를 살펴보자.

open(GRADES, “grades”) or die “Can’t open file grades: $!\n”;

파일을 여는 데 성공하면 다음 줄을 실행한다. 만약 파일을 여는 데 실패하면 die문장 이하의 오류 메시지를 출력하고 프로그램 실행이 중단된다.

글자 뜻대로, 위의 문장은 “grade를 열거나 혹은 종료!”가 된다. 다른 자연어의 예를 제외하고라도, 이러한 단락 판별 연산자를 사용하면 프로그램의 흐름을 눈으로 알아보기 쉽게 해 준다. 중요한 처리는 화면의 왼쪽에 위치하고 두 번째 처리는 화면의 오른쪽에 위치하게끔 하는 것이다.(위에서 $!변수는 운영체제에서 표시하는 오류 메시지를 담고 있다. 2장 특수 변수 참조) 물론 이들 논리 연산자는 전통적인 조건문, 즉 ifwhile에서도 사용될 수 있다.

비교 연산자

비교, 혹은 관계 연산자는 두 개의 스칼라 값(숫자나 문자열)의 상호 관계를 비교할 때 사용된다. 비교 연산자는 두 종류의 것이 있는데, 하나는 숫자를 비교하기 위한 것이고 다른 하나는 문자열을 비교하기 위한 것이다. 표 2-6은 $a와 $b가 각각 비교 연산자의 왼쪽, 오른쪽 피연산자라고 가정한 경우를 나타낸 것이다.

[표 2-5] 숫자, 문자열 비교 연산자

비교 숫자 문자열 결과
같다 == eq $a가 $b와 같으면 참
같지 않다 != ne $a가 $b와 같지 않으면 참
작다 < lt $a가 $b보다 작으면 참
크다 > gt $a가 $b보다 크면 참
작거나 같다 <= le $a가 $b보다 크지 않으면 참
비고 <=> cmp 같으면 0, $a가 크면 1, $b가 크면 -1
표 2-6의 마지막 연산자인 <=>와 cmp는 다른 연산자와 중복되지만, 때때로 sort 서브루틴과 같은 경우에 매우 유용하게 사용되기도 한다.(3장 참조).

파일 테스트 연산자

파일 테스트 연산자는 특정 파일에 대해 어떤 일을 하기 이전에 파일에 대한 다양한 정보를 알아낼 수 있도록 해 준다. 예를 들어 /etc/passwd라는 파일이 있는지 미리 알아본 후에 없으면 새로운 파일을 만드는 것이, 무턱대고 만들어서 기존에 갖고 있던 정보를 지우는 것보다 나을 것이다. 표 2-7에 이러한 파일 테스트 연산자의 예를 보였다.

[표 1-6] 파일 테스트 연산자

이름 결과
-e $a 존재 여부 $a 파일이 존재하면 참
-r $a 읽기 가능 $a 파일이 읽기 가능하면 참
-w $a 쓰기 가능 $a 파일이 쓰기 가능하면 참
-d $a 디렉토리 $a 가 디렉토리이면 참
-f $a 파일 $a 파일이 일반 파일이면 참
-T $a 텍스트 파일 $a 파일이 텍스트 파일이면 참
여기 몇가지 예제가 있다:

-e “/usr/bin/perl” or warn “Perl is improperly installed\n”;

-f “/vmunix” and print “Congrats, we seem to be running BSD Unix\n”;

여기서 “일반 파일”은 “텍스트 파일”과 다르다는 점에 주목하기 바란다. /vmunix와 같은 이진 파일은 일반 파일에 속하지만 텍스트 파일은 아니다. 텍스트 파일은 이진 파일의 상대적 개념이고, 일반 파일은 디렉토리나 장치 파일(device file)과 같은 것의 상대적 개념을 뜻한다.

이 외에도 상당히 많은 수의 파일 테스트 연산자가 있다. 대부분의 파일 테스트 연산자는 일원 불린(Boolean) 연산자이다. 즉, 하나의 피연산자를 취하며 피연산자는 파일이나 파일 핸들이 되고, 그 반환값으로 참 혹은 거짓을 갖는다. 파일 테스트 연산자 중 일부는 파일의 크기나 시간 등 좀 더 자세한 값을 반환하는 경우도 있다.

제어 구조

지금까지 성적 처리 프로그램을 제외한 모든 예제는 선형구조였다. 즉, 프로그램 내의 순서대로 각 명령어가 실행되는 구조였다. 또한 몇몇 예에서는 단락 판별 연산자를 이용하여 상황에 따라 하나의 명령어가 실행되든지 혹은 실행되지 않도록 하는 것도 보았다. 물론 이러한 선형적인 프로그램도 매우 유용하지만(실제로 많은 수의 CGI 프로그램이 이에 해당한다), 조건식이나 루프 등을 사용한다면 보다 훌륭한 프로그램을 만들 수 있다. 이러한 관점에서 Perl을 제어 언어라고도 할 수 있다.

프로그램 내에서 제어를 하려면 먼저 무엇인가를 판정해야 하고, 무엇인가를 판정하기에 앞서 무엇이 참이고 무엇이 거짓인지를 알아야 할 것이다.

참이란?

앞의 예에서 몇몇 연산이 참이나 거짓을 반환한다고 했었다. 더 자세히 들어가기 전에 참에 대해 알아보도록 하자. Perl은 다른 대부분의 컴퓨터 언어와는 조금 다르게 참을 다루지만, Perl로 프로그래밍을 하다 보면 그 의미를 파악할 수 있을 것이다.

Perl에서의 참은 항상 스칼라 구문 내에서 평가된다. 따라서 스칼라가 가질 수 있는 다음과 같은 값들에 대해 참과 거짓이 정의된다.

1. “”과 “0”을 제외한 모든 문자열은 참이다.
2. 0을 제외한 모든 숫자는 참이다.
3. 모든 레퍼런스는 참이다.
4. 그 밖에 정의되지 않은 모든 값은 거짓이다.

실제로 3번과 4번 항은 1, 2번에서 유추할 수 있다. 임의의 레퍼런스(3번째 법칙)는 무엇인가를 가리키고 있는 번지를 담고 있고, 0이 될 수 없으므로 참이다. 또한 정의되지 않은 값은 항상 0이거나 널(Null) 문자열이므로 1번에 의해 거짓이 된다.

또한 만약 모든 것이 문자열이라는 전제하에서 2번 항도 1번으로부터 이끌어 낼 수 있다. 참과 거짓을 판별할 때 숫자에서 문자열로의 강제 전환이 이루어진다면, 숫자 0은 문자열 “0”이 될 것이므로 거짓이 된다. 그 외의 모든 숫자는 문자열 “0”이 될 수 없으므로 참이 될 것이다. 다음 예로 앞서 설명한 참과 거짓의 경우를 자세히 살펴보자.

0 # 문자열 “0”이 될 것이므로, 거짓
1 # 문자열 “1”이 될 것이므로, 참
10 – 10 # 10-10 은 0, 즉 문자열 “0”이므로, 거짓
0.00 # 0이 되고, 문자열 “0”이므로, 거짓
“0” # 문자열 “0”이므로, 거짓
“” # 널 문자열이므로, 거짓
“0.00” # 널 문자열도 아니고 문자열 “0”도 아니므로, 참
“0.00” + 0 # + 연산자에 의해 강제로 숫자 0이 되므로, 거짓
\$a # $a의 레퍼런스이므로, 참(비록 $a가 거짓이어도)
undef() # 정의되지 않은 값을 반환하는 함수이므로, 거짓

if 와 unless

앞서 논리 연산자가 조건문을 만드는 경우를 살펴보았다. 조금 더 복잡한 형태의 논리 연산을 행하는 것이 바로 if이다. if는 주어진 조건을 판단하여 조건이 참일 경우 if를 따르는 블록을 실행하도록 한다.

블록은 하나 혹은 그 이상의 문장으로 이루어져 있으며 중괄호 {}로 묶여 있다. if문이 해당 블록을 실행하기 때문에 if를 사용할 때에는 반드시 중괄호를 사용해야만 한다. C언어에서는 실행해야 할 코드가 한 줄일 경우 중괄호를 생략할 수 있으나 Perl에서는 그럴 수 없다.

if ($debug_level > 0) {    # Something has gone wrong. Tell the user.
    print “Debug: Danger, Will Robinson, danger!\n”;
    print “Debug: Answer was ’54’, expected ’42’.\n”;
}

때로는 주어진 조건을 만족하여 해당 블록을 실행하는 것으로 충분하지 않는 경우가 있다. 그리고 주어진 조건을 만족하지 않을 때 다른 블록을 실행하도록 할 필요도 있을 수 있다. 물론 if를 두 번 사용해서 이러한 문제를 해결할 수도 있겠지만, Perl에서는 이런 경우 좀 더 간결한 방식을 사용한다. if에서 한 블록 다음에 옵션으로 else라고 하는 두 번째 조건문을 위치하여 주어진 조건이 거짓일 때 바로 다음의 블록을 실행하게끔 한다.

또한, 두 개 이상의 가지수를 갖는 조건문을 사용해야 할 경우도 있다. 이 경우 각 조건식에 해당하는 elsif를 계속 사용하여 문제를 해결할 수 있다.

if ($city eq “New York”) {
    print “New York is northeast of Washington, D.C.\n”;
}
elsif ($city eq “Chicago”) {
    print “Chicago is northwest of Washington, D.C.\n”;
}
elsif ($city eq “Miami”) {
    print “Miami is south of Washington, D.C.  And much warmer!\n”;
}
else {
    print “I don’t know where $city is, sorry.\n”;
}

ifelsif는 순서대로 어느 하나가 참이 될 때까지 판별되며, 아무것도 참이 되지 않으면 else이하의 블록이 실행된다. 만약 이들 조건 중 어느 하나가 참이 되면 해당 조건 바로 다음의 블록이 실행되며 나머지 모든 블록은 건너뛰게 된다. 만약 if문에 해당하는 조건이 참인 경우에는 아무것도 하지 않고 거짓일 경우에만 실행하도록 하기 위해서는 if-else문에서 빈 블록을 사용하는 방법, if 조건을 반대로 하는 방법(“이것이 참이 아닐 경우에 무엇인가를 실행하라”)등이 있을 수 있으나 이런 경우에는 다음과 같이 unless를 사용하면 된다.

unless ($destination eq $home) {
    print “I’m not going home.\n”;
}

반복(루프) 구문

Perl은 네 가지 반복 구문을 제공한다: while, until, for, foreach. 이들 구문은 같은 코드에 대해 반복 실행하도록 해 준다.

while과 until 루프

whileuntil 루프는 ifunless의 관계와 비슷하다. 우선 해당 구문의 조건식을 판별한다. 주어진 조건이 만족되면(즉 while에서 참, until에서는 거짓) 해당 블록이 실행된다.

while ($tickets_sold < 10000) {
    $available = 10000 $tickets_sold;
    print “$available tickets are available.  How many would you like: “;
    $purchase = <STDIN>;
    chomp($purchase);
    $tickets_sold += $purchase;
}

만약 처음부터 주어진 조건이 만족되지 않으면 루프는 한 번도 실행되지 않음을 주목하기 바란다. 예를 들어 이미 10,000개의 티켓을 팔았다면 while 루프 다음에 다음과 같은 줄을 넣어야 할 것이다.

print “This show is sold out, please come back later.\n”;

앞서 성적 처리 예제의 4번째 줄은 다음과 같다.

while ($line = <GRADES>) {

괄호 안의 대입 연산자에 의해 GRADES 파일 핸들에 해당하는 파일의 다음 줄을 읽어서 $line변수에 대입한 후 $line의 값이 반환되므로, while 조건문에서는 $line의 값이 참인가 거짓인가에 따라 다음 블록의 실행여부를 결정짓게 된다. 여러분 중에는 파일의 빈 줄이 입력되면 while 조건문이 거짓으로 판별하게 되지 않겠느냐고 생각하는 사람도 있을 지 모른다. 그러나 정답은 “아니다”. 앞서 말한 대로 한 줄씩 입력되는 경우 빈 줄은 “\n”에 해당하는 줄바꿈 글자를 포함하고 있기 때문이다. 물론 “\n”는 앞서 참과 거짓의 정의에 따르면 거짓이 아니라 참이다. 따라서 주어진 조건을 만족하여 빈 줄이 중간에 있을 지라도 루프는 계속 실행되게 된다.

한편, 파일의 마지막에 도달하면 줄 입력 연산자(<>)는 정의되지 않은 값을 반환하므로 조건식은 거짓이 되어 루프의 실행이 종료된다. 따라서 Perl에서는 특별히 eof 함수를 사용하여 테스트를 할 필요가 없다.

실제로 Perl의 조건문에서는 모든 것이 자연스럽게 동작하도록 디자인되어 있다. 예를 들어 스칼라를 포함하는 배열은 자신의 길이를 반환하도록 되어 있으므로

while (@ARGV) {
        process(shift @ARGV);
}

위와 같은 예에서 @ARGV의 내용이 모두 없어질 때까지 루프가 실행된다.

for 문

다른 형태의 반복문은 for 루프이다. for 루프는 while 루프처럼 동작하지만 몇 가지 점에서 조금 다르다.(C 프로그래머는 쉽게 이해할 수 있을 것이다.)

for ($sold = 0; $sold < 10000; $sold += $purchase) {
    $available = 10000 $sold;
    print “$available tickets are available. How many would you like: “;
    $purchase = <STDIN>;
    chomp($purchase);
}

for 루프의 조건에는 최대 3개의 식을 적을 수 있다. 루프 변수의 초기 상태를 정하는 식, 루프 변수의 상태를 테스트하는 식, 그리고 루프 변수의 상태를 변화시키는 식이 바로 그것이다. 루프가 시작되면 초기 상태를 설정하고 조건식을 판별한다. 조건식의 값이 참이면 블록이 실행된다. 블록의 끝까지 실행되고 나면 조건식을 다시 판별하여 역시 참이면 블록을 다시 반복한다. 조건이 참인 한 블록의 실행과 변수 상태의 변화는 계속 이루어진다.

foreach 문

Perl의 반복문 중 마지막은 foreach 문이다. foreach 는 배열 등과 같은 스칼라 값의 모음에 같은 코드를 반복 실행하고자 할 때 사용한다.

foreach $user (@users) {
    if (-f “$home{$user}/.nexrc”) {
        print “$user is cool… they use a perl-aware vi!\n”;
    }
}

foreach문에서 괄호 안의 식은 리스트이다. 리스트의 각 요소가 하나씩 루프 변수의 값으로 대입되어 루프의 블록이 실행된다. 이 경우 루프 변수는 리스트 요소의 복사값이 아니라 리스트 값 바로 그 자체이므로, 루프 내에서 루프 변수의 값을 바꾸면 원래 배열의 값을 바꾸는 것이 된다.

아마 전형적인 Perl 프로그램에서는 for 루프보다 foreach 루프를 많이 보게 될 것이다. Perl에서는 foreach문에서 필요한 리스트를 만들어 내는 것이 무척 쉽기 때문이다. 해쉬의 키를 정렬하여 매 키 값마다 루프를 실행하는 다음과 같은 예제도 Perl에서 흔히 볼 수 있는 것 중 하나이다.

foreach $key (sort keys %hash) {

실제로 성적 처리 예제의 9번째 줄은 바로 위의 예제에 해당하는 것이다.

실행 중지: next와 last

nextlast 키워드는 루프 안에서의 흐름을 바꿀 수 있도록 해 준다. 이것은 특별한 경우가 아니다. 필요에 따라 루프의 실행을 건너 뛰어야 할 때도, 혹은 루프 전체를 빠져 나가야 할 때도 있을 수 있다. 예를 들어 UNIX 계정을 처리하는 경우, root나 lp같이 시스템이 사용하는 계정은 건너 뛰어야 할 때, next 키워드를 이용하여 현재 루프의 맨 마지막으로 옮겨 다음 번 루프를 실행하도록 할 수 있다. last 키워드는 전체 블록의 맨 마지막으로 건너 뛰어 마치 조건식이 거짓을 반환한 것처럼 루프를 빠져 나오게끔 한다. 예를 들어 아래 예와 같이 특별한 계정을 찾는 즉시 루프를 빠져 나오도록 하고 싶을 때에 이들 연산자를 사용하면 된다.

foreach $user (@users) {
    if ($user eq “root” or $user eq “lp”) {
        next;
    }
    if ($user eq “special”) {
        print “Found the special account.\n”;
        # do some processing
        last;
    }
}

또한 루프에 레이블을 지정한 다음, 필요한 경우 여러 단계의 루프를 한꺼번에 빠져 나오도록 할 수도 있다. 구문 변환자(아직 설명하지 않았음)와 함께 사용하면 매우 효과적으로 루프를 빠져 나오도록 할 수 있다. 다음 예를 보자.

LINE: while ($line = <ARTICLE>) {
    last LINE if $line eq “\n”;                        # 첫번째 줄이 나오면 종료
    next LINE if $line =~ /^#/;                       # 주석을 건너뛰기
    # your ad here
}

여러분은 혹시 “잠깐, ^# 어쩌고 하는 부분이 무엇을 뜻하지요?” 하고 물을 지도 모른다. 이 부분은 정규 표현식(regular expression)을 포함한 패턴 일치 식으로서 다음 절에서 다룰 것이다. 무엇보다도 Perl은 텍스트 처리 언어로서, 정규 표현식은 Perl의 텍스트 처리의 핵심이다.

정규 표현식(Regular Expression)

정규 표현식은 grep, sed, awk등과 같은 많은 수의 UNIX 프로그램, vi, emacs와 같은 문서 편집기, 그리고 쉘 등과 같이 다양한 곳에서 사용되고 있다. 정규 표현식을 사용하면 복잡한 문자열을 간단한 방법으로 표현할 수 있다.

Perl에서는 정규 표현식을 여러 가지 경우에 사용한다. 가장 흔한 예는, 특정한 문자열이 주어진 패턴과 일치하는 지 판단하는 경우이다.예를 들어 /foo/ 와 같은 것은 패턴 일치 연산자로서, 주어진 문자열 내에 “foo”가 포함되어 있는 지를 판단하는 경우에 사용된다.

두 번째는, 문자열 내에서 특정 패턴을 찾은 후 원하는 다른 문자열로 바꿀 때이다. 프로그램 내에서 s/foo/bar와 같이 나타내면 “foo”라는 문자열을 찾고, “foo”대신 “bar”로 바꾸라는 뜻이다. 이것을 치환 연산자라고도 한다.

마지막으로, split 연산자를 사용하여 주어진 데이터를 원하는 단위로 쪼개는 데에도 정규 표현식을 사용한다. 이 때 데이터를 구분하는 단위를 구획 문자(delimiter) 라고 하며, 구획 문자를 정의하는데 정규 표현식을 사용한다. 앞서 성적 처리 예제의 5번째 줄과 12번째 줄에서 공백 문자를 구획 문자로 사용하여 각 줄에서 이름과 성적을 뽑아 내는 데 split 연산자를 사용했었다. 이와 같이 정규 표현식을 사용하여 임의의 구획 문자를 지정, 주어진 문자열을 쪼갤 수 있다.

(패턴 일치를 판단하는 경우 영문자의 대소문자를 구별하지 않는 등, 다양한 방법으로 패턴 일치를 할 수 있게끔 하는 여러 가지 변환자(modifier)가 있다. 나중에 2장에서 자세히 다룰 것이다.)

정규 표현식의 가장 간단한 사용 예는 주어진 글자가 일치하는 지의 여부를 판단하는 경우이다. 만약 문자열과 같이 여러 글자가 주어졌을 경우, 서브 문자열과 같이 해당 문자열이 포함되어 있는 지의 여부를 판별하게 된다. 예를 들어 주어진 HTML 파일에서 다른 HTML 파일로의 연결이 되어 있는 곳을 모두 찾아서 나타내고자 할 때, 이러한 연결은 http:라는 문자열이 항상 나타나므로 다음과 같이 찾을 수 있다.

while ($line = <FILE> ) {
        if ($line =~ /http:/) {
                      print $line;
        }
}

여기서 =~는 패턴 일치 연산자로서 주어진 정규 표현식 http:이 $line과 일치하는 지 살펴 본다. 만약 $line에 “http:”와 일치하는 문자열이 있으면, if 판별식은 참을 반환하고 print명령에 해당하는 블록이 실행된다. 만약 =~ 연산자를 사용하지 않으면, Perl은 $line대신 기본 변수인 $_에서 정규 표현식을 판별한다. Perl에서는 대부분의 연산자가 기본적으로 $_를 사용하도록 되어 있으므로, 위의 예제는 다음과 같이 간략하게 만들 수 있다.

while (<FILE>) {
        print if /http:/;
}

위와 같이 간단한 경우 외에, HTTP연결 외의 다른 모든 연결을 찾아야 한다면 어떻게 해야 할까? 예를 들어 “http:”, “ftp:”, “mailto:”등을 모두 찾는 경우, 찾아야 할 연결이 하나 늘어날 때마다 비교해 볼 내용이 다음과 같이 늘어나야 할 것이다.

while (<FILE>) {
        print if /http:/;
        print if /ftp:/;
        print if /mailto:/;
}

정규 표현식을 사용하여 위의 경우를 다음과 같이 단순화할 수 있다. 즉 몇 개의 알파벳으로 이루어진 단어 뒤에 콜론(:)이 오는 문자열(http:, ftp:, mailto: 등)을 찾는 경우이므로 정규 표현식으로는 /[a-zA-Z]+:/와 같이 나타낼 수 있다. 여기서 대괄호([ ])는 문자 클래스(character class)를 의미한다. a-z와 A-Z는 모든 알파벳의 대, 소문자를 가리키는 것으로서, -기호는 양쪽 글자를 포함하여 그 사이에 있는 모든 글자를 나타낼 때 사용한다. 그리고 +는 특별한 글자로서, “+앞에 있는 글자가 하나 혹은 그 이상”을 나타낸다. 이렇게 특정한 무엇인가를 반복하도록 하는 것을 수량자(quantifier)라고 한다.( “/”는 정규 표현식의 일부가 아니라 패턴 일치 연산자의 일부로서, 정규 표현식을 포함하고 있다는 것을 알려 주는 역할을 한다.)

알파벳과 같이 자주 사용되는 클래스에 대해 Perl에서는 사용하기 편리하도록 미리 정의해 놓았는데 표 1-7이 그것이다.

[표 1-7] 정규 표현 문자 클래스

이름 정의 코드
공백 [\t\n\r\f] \s
문자 [a~zA~Z0~9] \w
숫자 [0~9] \d
위 클래스는 “한 글자와의 일치 여부”를 판단하는 데 사용됨을 주의하기 바란다. 즉 \w는 한 단어와 일치하는 지의 여부를 판단하는 것이 아니라, 일반적인 단어를 구성하는 영문자 한 글자의 일치 여부를 판단하는 데 사용된다.(따라서 단어 일치를 판별하려면 앞서의 +수량자를 사용하여 \w+와 같이 나타내면 된다.) 또한 Perl에서는 이들 문자 클래스의 글자를 대문자로 하면 그 의미가 반대로 되는데, 예를 들어 숫자가 아닌 경우는 \D와 같이 나타내면 된다.

(\w가 [a-zA-Z_0-9]와 일치하지 않을 수도 있다는 점을 주의하기 바란다. 몇몇 로케일(locale)에서는 일반적인 ASCII외의 글자를 사용할 수도 있는데, 이 경우에도 \w는 단어 글자를 나타내는 데 사용된다.)

이 밖에 아주 특별한 문자 클래스로서 “.”가 있다. “.”는 임의의 한 글자와 일치함을 나타낸다. 예를 들어, /a./이라고 했을 경우 a를 포함한 문자열 중 a다음에 어느 한 글자가 따라 오는 것을 의미한다. 따라서 “at”나 “am”, “a+”등과는 일치하지만, “a”는 a다음에 .에 해당하는 글자가 없기 때문에 일치하지 않는다. 이러한 패턴은 문자열 내에 임의의 위치에 존재할 수 있으므로, “oasis”나 “camel”과도 일치하나 “sheba”와는 일치하지 않는다. “caravan”의 경우 첫 번째 a와 두 번째 a 모두 일치하지만, 첫 번째 a에서 일치하는 것으로 패턴 일치를 마치게 된다.

수량자(Quantifier)

앞서 언급한 문자, 혹은 문자 클래스는 한 글자의 일치 여부를 판별하기 위한 것이다. 앞서 예에서 보았듯이, 여러 개의 단어 글자의 일치 여부를 알아 보기 위해서 \w+를 사용했었다. 여기서 +는 수량자의 일종으로서, 이 밖에도 여러 가지 수량자가 존재한다. (이들 수량자는 그 개수를 나타내기 위한 아이템 바로 뒤에 위치한다.)

가장 일반적인 수량자는 특정한 아이템이 일치하는 최소와 최대 횟수를 지정하는 것이다. 이 경우 {}안에 쉼표로 구분된 두 개의 숫자로 최소와 최대 횟수를 표시한다. 예를 들어 북미지역의 전화번호를 나타낼 때, /\d{7,11}/과 같이 하여 최소 숫자 7개, 최대 숫자 11개로 이루어진 전화번호를 표시할 수 있다. 만약 {}안에 숫자를 하나만 적으면 최소와 최대 횟수를 동일하게 하는 것으로, 즉 {}앞의 아이템이 정확하게 숫자 만큼 일치하는 것을 나타낸다.

만약 최소와 쉼표를 적고 최대를 적지 않는 경우는 최대 횟수가 무한대가 된다. 다시 말해서, 최소 숫자 이상 임의의 숫자 만큼 일치하는 것을 의미한다. 예를 들어 /\d{7}/ 이 국번을 포함한 전화번호 7자리를 의미하는 데 비해 /\d{7,}/은 7자리 이상의 모든 전화번호를 나타내는 것이다. 이와 반대로 최대 몇 번 같은 글자의 일치를 표현하는 방법은 없으므로, /.{0,5}/와 같이 임의의 글자가 최대 5번 나타나는 것으로 대신해야 한다.

이와 같은 최소·최대값에 대한 조합이 많기 때문에 Perl에서는 이들 중 몇 가지를 미리 정의해 놓았다. 앞서 본 +는 {1,} 즉 “바로 앞의 아이템이 최소 1번 이상”을 나타내는 것이다. 또한 *는 {0,} 즉 “바로 앞의 아이템이 0번, 혹은 그 이상”을 나타내는 데 사용된다. 그리고 ?는 {0,1}로서 “바로 앞의 아이템이 없거나, 혹은 1번 있음”을 표시하는 것이다.

이러한 수량자를 사용할 때 주의해야 할 점이 있다. 이들 수량자는 주어진 문자열에서 가능한 한 많은 수의 글자와 일치하려고 한다는 것이다. 예를 들어 /\d+/를 “1234567890”과 일치시킬 경우, /\d+/는 전체 문자열과 일치하는 결과를 가져 온다. 또한 “.”를 사용하여 일치 여부를 판별할 때, 수량자의 사용이 예상치 못했던 결과를 가져 오기도 한다. 예를 들어,

spp:Fe+H20=FeO2;H:2112:100:Stephen P Potter:/home/spp:/bin/tcsh

와 같은 문자열이 주어지고 “spp”를 찾기 위해 /.+:/를 사용할 경우, +수량자는 앞서 말한 대로 가능한 한 많은 수의 글자와 일치하려고 하기 때문에 결과적으로 spp부터 /home/spp:까지의 긴 문자열 전체와 일치하는 결과를 가져 온다. 이런 결과를 피하기 위해서는 부정 문자 클래스, 즉 /[^:]+:/와 같이 나타내면 된다. 다시 말해 “하나 혹은 그 이상의 “:”이 아닌 글자와 일치한 다음, 마지막에 :과 일치”하는 문자열을 찾게 하는 것이다. 여기서 ^는 []로 둘러 싸인 문자 클래스 전체를 부정하는 역할을 한다. 정규 표현식의 사용에 있어서 또 다른 주의할 점은, 가능한 한 앞쪽에서 일치가 일어난다는 사실이다. 이것은 바로 위에서 표시했던 가능한 한 많은 수의 글자와 일치하는 것보다 우선 순위가 높게 동작한다. 문자열에서의 일치는 왼쪽에서 오른쪽으로 이루어지므로, 주어진 문자열의 오른쪽에 더 길게 일치할 수 있더라도 문자열의 왼쪽에 그 패턴이 존재하면 그 곳에서 일치가 이루어진다는 사실이다. 예를 들어 치환 명령(s///)을 사용하여 기본 변수 영역($_)에서 “x”로 이루어진 문자열을 없애고 싶을 경우,

$_ = “fred xxxxxxx barney”;

s/x*//;

위와 같이 하면 결과적으로는 아무런 일도 일어나지 않는다. 왜냐하면, x*(0 혹은 그 이상의 “x”)에 의해 문자열의 시작 부분에서 “f”가 나오기도 전에 무조건 일치하기 때문이다.

또 알아야 할 것이 있다. 수량자는 바로 앞의 한 글자에만 적용되기 때문에 /bam{2}/ 는 “bamm”과는 일치하나 “bambam”과는 일치하지 않는다. 만약 한 글자 이상과 일치하려면 괄호를 사용해서 /(bam){2}/과 같이 묶어 주어야 한다.

최소 일치

만약 구 버전의 Perl을 사용하면서 가능한 한 많은 글자 수와 일치하는 기능을 사용하고 싶지 않다면 부정 문자 클래스를 사용하여야 한다. 최신 버전의 Perl에서는 임의의 수량자 뒤에 물음표(?)를 추가하여, 가능한 많은 글자수와 일치하지 않도록(최소한의 글자수와 일치하도록) 지정할 수 있다. 앞서의 spp라는 사용자 문자열 일치의 경우, /.*?:/ 와 같이 할 수 있다. 즉 .*?은 .*와 달리 가능한 한 적은 숫자의 글자와 일치하기 때문에 첫 번째 콜론까지(spp:)만 일치하게 된다. 일치 경계 제한특정 패턴의 일치 여부를 판별할 경우, 주어진 문자열에서 패턴이 일치하는 모든 부분에 대해 패턴의 일치 여부를 찾게 된다. 이 경우 앵커(anchor)를 사용해서 패턴이 문자열에서 일치하는 부분을 제한할 수 있다. 앵커 자체는 무엇과도 일치하지는 않지만, 앵커가 둘러싸고 있는 패턴의 일치를 구체적으로 제한한다는 것을 주목하기 바란다.

특수 문자열 \b는 단어의 경계를 나타낸다. 예를 들어

/\bFred\b/

는 “The Great Fred”나 “Fred the Great”와는 일치하지만 “Frederick the Great”와는 일치하지 않는다. 왜나하면, “Frederick”에서의 “de”가 단어 경계를 포함하고 있지 않기 때문이다.

문자열의 시작과 끝을 나타내기 위한 앵커도 있다. 캐럿(^)은 문자열의 시작을 의미한다. 예를 들어 /^Fred/는 “Frederick the Great”와는 일치하지만 “The Great Fred”와는 일치하지 않는다. 달러 표시($)는 문자열의 끝을 나타낸다.

따라서 다음과 같이 했을 경우,

next LINE if $line =~ /^#/;

윗 줄은 ‘만약 이 줄이 #으로 시작하면 LINE 루프의 다음으로 가라‘라는 것을 의미한다.(역자주: #으로 시작하는 주석문을 처리할 때 사용된다)

백 레퍼런스

앞서 언급했듯이 수량자를 사용할 경우 한 글자 이상에 대해 적용할 때는 괄호를 사용해야 했다. 정규 표현식 내의 괄호는 이 밖에도 일치된 패턴을 기억했다가 나중에 재참조할 경우에도 사용된다. 이 경우 괄호를 사용했을 때와 사용하지 않았을 때 패턴의 일치에는 전혀 차이가 없다. 예를 들어 /\d+/와 /(\d+)/는 가능한 한 많은 하나 이상의 숫자와 일치한다는 점에서는 차이가 없다. 그러나 두 번째만이 프로그램 내에서 일치된 패턴을 나중에 다시 참조하고 싶을 경우 사용할 수 있다.

괄호를 사용해 기억된 패턴을 나중에 어떻게 다시 참조하는가는 재참조하는 위치에 따라 달라진다. 같은 정규 표현식 내에서는 역슬래쉬(\) 다음에 숫자를 적어 주어진 정규 표현식의 처음부터 괄호의 순서대로 재참조할 수 있다. 예를 들어 HTML태그와 같은 문자열을 찾을 경우, 주어진 문자열이 “<B>Bold</B>”라면 정규 표현식으로는 /<(.*?)>.*?<\/\1>/과 같이 나타낼 수 있다. 따라서 \1로 나타낸 부분은 앞서의 (.*?)에서 나타낸 부분과 같은 “B”를 찾게 된다.

정규 표현식 밖에서는 $뒤에 숫자를 적어 표시한 스칼라 변수로 재참조를 지정한다. 예를 들어 문자열에서 처음 두 단어의 위치를 바꾸고 싶을 경우 간단하게

s/(\S+)\s+(\S+)/$2 $1/

와 같이 하면 된다.

위의 예에서 보이듯이 변수 치환과 정규 표현식을 이용한 패턴 일치로 인해 Perl이 텍스트 처리에 뛰어나다는 점을 알 수 있다.

리스트 처리

이 장의 앞 부분에서 Perl에는 스칼라(단수를 표시하는 데 사용)와 리스트(복수를 표시하는데 사용)가 있다고 언급했었다. 대부분의 전통적인 연산자들은 스칼라를 처리하는데 사용된다. 이들은 한 개의 인수(이진 연산자의 경우 두 개의 인수)를 취하며, 심지어 리스트 연산의 경우에도 단수의 결과값을 갖는다.

예를 들어,

@array = (1 + 2, 3 – 4, 5 * 6, 7 / 8);

과 같은 경우, 우변에는 정확하게 4개의 값을 가짐을 알 수 있다. +, -와 같은 일반적인 수학 연산자는 결과값으로 항상 스칼라를 내놓기 때문이다. 따라서 배열에 값을 대입하는 리스트 연산에서도 이러한 단수 결과값이 적용된다.

그러나 몇몇 Perl연산자의 경우 사용되는 구문에 따라 스칼라 값을 만들거나 리스트 값을 만들기도 한다. 이 경우 스칼라 또는 리스트 값 중 어느 결과가 필요한 지는 자동으로 결정된다. 다음과 같은 몇 가지 개념을 이해하면 어떤 경우에 스칼라, 혹은 리스트 값이 생성되는지 알 수 있을 것이다.

첫째로, 리스트는 무엇인가에 “둘러 싸여” 만들어 진다. 위의 예의 경우, 리스트 대입 연산이 그것이다. 다음 2장과 3장의 많은 예에서 LIST를 인수로 갖는 다양한 연산자를 볼 수 있을 것이다. 이러한 연산자는 리스트 구문을 만들어 내는 연산자이다. 이 책에서는 LIST를 “리스트 구문을 만드는 문법적 구조체”라는 기술적 용어로 정의하도록 하겠다. 예를 들어 sort의 경우 다음과 같은 문법 요약을 찾을 수 있을 것이다.

sort LIST

이 경우 sort는 리스트를 인수로 전해 준다.

둘째로, 컴파일시에, LIST를 갖는 임의의 연산자는 LIST의 각 요소에게 리스트 구문을 제공한다. 따라서 LIST내의 최상위 연산자나 요소는 리스트를 만들어 내야 한다는 것을 알고 있다. 예를 들어,

sort @guys, @gals, other();

라고 했을 경우, @guys, @gals, other()는 각각 리스트 값을 만들어 내야 한다는 것을 알고 있다는 뜻이다.

마지막으로, 실행시에, 각 LIST요소는 순서대로 리스트를 생성해 내며, 그리고 나서 (여기서부터가 중요함) 각각 구분된 리스트가 하나로 합해진 다음 하나의 큰 리스트로 만들어 진다. 그 결과 최종적으로 하나의 일차원 리스트가 LIST를 인수로 했던 함수에 전달된다.

앞의 예에서 만약 @guys가 (Fred,Barney)이었고, @gals가 (Wilma,Betty)였으며, others()함수가 하나의 요소를 갖는 리스트 (Dino)를 반환했다면 최종적으로 sort가 취하게 되는 LIST는 (Fred,Barney,Wilma,Betty,Dino)가 되며, sort가 반환하는 값은(Barney,Betty,Dino,Fred,Wilma)가 된다. 몇몇 연산자는 리스트를 만들어 내기도 하고(예: keys), 또 일부는 리스트를 받아들이기만 하며(예: print), 또 일부는 다른 형태의 리스트로 변환하기도 한다.(예: sort). 다른 형태의 리스트로 변환하는 연산자는 필터의 한 형태로 볼 수 있으며, 쉘에서와는 달리 데이터의 흐름은 오른쪽에서 왼쪽으로 진행한다. 이들 연산자는 인수를 오른쪽에서부터 취하기 때문이다. 다음과 같이 몇몇 리스트 연산자를 한 줄에 동시에 표기할 경우,

print reverse sort map {lc} keys %hash;

우선 %hash의 키 값 리스트를 얻은 후 map함수에 전달하고, map함수는 모든 키 값을 lc 연산자에 의해 모두 소문자로 변환한다. 그 다음 sort함수에 의해 순서대로 나열된 후, reverse함수에 의해 리스트 요소의 순서를 반대로 만들고 그 결과를 출력하게 되는 것이다.

여러분이 모르는 것

다치게 하고 싶지 않아요.(진심!)

휴… 다시 자연어로서 Perl에 대해서 생각할 시간이다. 자연어를 사용하는 사람들은 다른 숙련도를 가지고 있고, 그 언어를 말하고, 말하는 만큼 배우고, 그리고 일반적으로 언어를 잘 사용하게 된다는 것을 인정한다. 여러분이 영어의 모든 것을 알지 못하는 것처럼 Perl의 모든 것에 대해서 아직 알지 못한다. 그러나 Perl에서는 그것이면 족하다. 심지어 여러분에게 자기자신의 함수를 작성하는 것에 대해서 설명하지 않아도 Perl을 가지고 유용하게 프로그램을 만들 수 있다. 시스템 관리 언어나 네트웍 언어 또는 객체 지향 언어로서의 Perl에 대해서 설명조차 거의 하지 않았지만 말이다. 앞으로 이러한 것들에 대해서 쓸 것이다.(생각해 보니, 이미 쓴 것 같다)

그러나 마지막으로, 여러분은 Perl에 관한 여러분 자신의 관점을 만들기 바란다. 그것은 창조의 고통을 받는 아티스트로서의 여러분의 특권이다. 여러분에게 우리가 페인트 하는 방법을 설명하지 여러분이 페인트 하는 방법을 설명하지는 않는다. 왜냐하면 한가지 일을 하는데에는 많은 방법이 있기 때문이다.

 

 

Perl 자세히 들여다 보기

이 장에서는 Perl의 문법적인 면에 대해서 자세히 살펴보기로 한다. Perl의 각 함수에 대해서는 3장에서 자세히 다룰 것이며, 레퍼런스와 객체 등에 대해서는 그 뒤쪽의 장에서 다룰 예정이다.

문법적 측면

Perl은 대부분의 경우 자유롭게 표현할 수 있는 언어이다. 예외는 format 선언문과 따옴표로 인용한 문자열 정도이다. 주석문은 #로 시작한 줄의 끝까지 해당된다.

Perl은 ASCII 문자로 정의되어 있지만, 필요에 의해 ASCII 문자에 포함되어 있지 않은 문자를 문자열에 포함시켜야 할 경우도 있다. 알파벳이나 숫자, 공백 문자등이 아닌 비-ASCII 문자를 사용하기 위해 인용문 사용시 각종 구획 문자(demiliter)가 제공된다.

공백 문자는 토큰과 토큰 사이를 구별하는 데 필요하며, 모든 공백 문자는 이러한 용도로 사용된다. 또한 주석문도 편의상 달아 놓은 것으로, 실제로는 공백 문자로 간주된다. 개행문자는 인용 문자열(quoted string)이나 format 문 등의 내부에서만 띄어쓰기와 구별되며 그 밖의 경우는 공백 문자로 간주된다.

Perl의 문법적인 측면에서 특이한 점은, =로 시작되는 줄부터 =cut까지의 줄이 모두 무시된다는 사실이다. 이 부분은 POD(Plain Old Document)라고 하는 것으로서, Perl 배포본에는 이런 POD 주석문을 매뉴얼 페이지나 LaTex, HTML 문서로 변환해 주는 프로그램이 포함되어 있다.

기본 데이터 타입

컴파일 시에 얼마나 다양한 종류의 데이터 타입을 제공하는가는 컴퓨터 언어에 따라 매우 다양하게 나타난다. 비슷한 형태의 값을 나타내기 위해 많은 종류의 데이터 타입을 제공하는 대부분의 다른 컴퓨터 언어에 비해, Perl은 단순히 몇 가지의 데이터 타입만을 제공한다.(물론 프로그래머의 필요에 따라 Perl의 객체 지향 기능을 이용하여 다양한 형태의 데이터 타입을 만들 수 있다.) Perl에서 기본적으로 제공하는 데이터 타입은 스칼라, 스칼라로 이루어진 배열, 스칼라로 이루어진 해쉬, 이렇게 3가지이다.

스칼라는 가장 기초가 되는 데이터 타입으로서, 다른 복잡한 형태의 데이터 타입도 모두 스칼라로부터 만들어진다. 스칼라는 하나의 문자열이나 숫자 값과 같이 단순한 형태의 데이터를 말한다. 이러한 스칼라를 모아서 배열이나 해쉬 같은 조금 더 복잡한 형태의 데이터 타입을 구성하게 된다. 배열은 순서를 정해 나열한 스칼라의 모음으로서, 0부터 시작하는 숫자 첨자를 인덱스로 하여 그 값을 참조할 수 있다. 해쉬는 순서가 정해있지 않은, 키와 값을 한 쌍으로 하는 값의 모음으로서, 키에 해당하는 문자열을 인덱스로 그 값(스칼라)을 참조할 수 있다. 변수는 이들 세 가지(스칼라, 배열, 해쉬) 중 한 가지 타입으로 정해진다.(이들 외에 Perl에서 다른 형태의 데이터 타입으로 볼 수 있는 것들로는 파일 핸들, 디렉토리 핸들, 서브루틴, 타입글로브(typeglobe), 포맷(format)등이 있다.)

용어

앞에서 Perl의 데이터 타입을 알아 보았다. 지금부터는 데이터를 프로그램 내에서 표현하는 데 사용되는 다양한 형태의 Perl 용어에 대해 알아보도록 하겠다. 가장 먼저 우리가 살펴볼 것은 변수이다.

변수

앞서 언급했던 세 가지 데이터 타입에 해당하는 변수를 나타내기 위해서 Perl에서는 특수한 문자를 변수 이름 앞에 붙여야 한다. 스칼라 변수는 항상 $로 시작되는 변수명을 갖는다. 또한 배열이나 해쉬의 특정 요소(이것 역시 스칼라)를 참조할 때에도 마찬가지로 $로 시작하는 변수를 참조해야 한다. 이것은 마치 영어에서 대부분의 단어 앞에 붙는 정관사 “the”와 같다고 볼 수 있다. 정리하자면,

$days 스칼라 값 $days
$days[28] 배열 @days의 29번째 값
$days{‘Feb’} 해쉬 %days의 “Feb”키에 해당하는 값
$#days 배열 @days의 마지막 인덱스
$days->[28] $days가 가리키고 있는 배열의 29번째 값

배열 전체나 배열의 일부분, 해쉬의 일부분을 나타내는 변수는 @로 구별한다.

@days ($days[0], $days[1], … $days[n]) 과 같다
@days[3..5] ($days[3], $days[4], $days[5])와 같다
@days[3..5] @days[3,4,5]와 같다
@days{“Jan”, “Feb”} ($days{‘Jan’}, $days{‘Feb’})와 같다

해쉬 전체는 %를 이용해 변수를 나타낸다.

%days (Jan => 31, Feb => $sleep ? 29 : 28, …)

위에서 열거한 9개의 예와 같은 변수들은 lvalue로 사용되어 특정한 값을 대입하는 데에 사용할 수 있다.

이 밖에 서부루틴 호출시 사용하는 시작 문자 &나 심볼 테이블 엔트리를 나타내는 시작 문자 *도 있으나 이들에 대해서는 나중에 설명하기로 하겠다.

각 변수 타입은 각각의 이름 영역(namespace)을 갖고 있다. 다시 말해서 같은 이름으로 스칼라 변수, 배열, 해쉬를 서로 충돌 없이 정의해서 사용할 수 있다는 것이다. 즉, $foo와 @foo는 서로 다른 값이며, $foo[1]은 @foo의 한 요소를 말하는 것이지 $foo의 일부분을 말하는 것은 아니라는 뜻이다.

변수명은 항상 $, @, %으로 시작하기 때문에 Perl에서의 예약어(reserved word)와 충돌하지는 않지만, 레이블이나 파일 핸들 등은 이러한 기호로 시작하지 않으므로 Perl의 예약어와 충돌할 수도 있다. Perl에서 예약어는 모두 소문자로 되어 있으므로, 레이블이나 파일 핸들의 이름을 모두 소문자로만 하지 않으면 이러한 충돌은 피할 수 있다. 예를 들어 open(log, ‘logfile’)보다는 open(LOG, ‘logfile’)이라고 하는 쪽이 안전하다는 뜻이다. 파일 핸들의 이름을 대문자로 사용하면 가독성을 높일 수 있을 뿐 아니라 다른 예약어와의 충돌도 피할 수 있다.

영문 대, 소문자의 구별도 중요하다. FOO와 Foo, foo는 모두 다른 이름이다. 이름은 영문 대, 소문자나 _(underscore, 밑줄문자)로 시작하며 1 ~ 255까지의 길이를 가질 수 있다. 또한 첫 글자 외에는 영문 대소문자나 숫자, 그리고 _를 포함할 수 있다. 숫자로 시작되는 이름은 숫자로만 이루어져야 한다. 영문 대소문자, _, 숫자 외의 다른 글자로 시작되는 이름은 그 다음에 한 글자만 올 수 있으며(예: $? 혹은 $$) 이들 대부분은 Perl에서 미리 지정된 중요한 뜻을 갖고 있는 것들이다. 예를 들어 Bourne 쉘에서와 같이 $$는 현재의 프로세스 ID를 의미하고 $?는 가장 최근의 자(child) 프로세스의 종료(exit) 상태를 나타낸다.

때때로 이름을 간접적으로 참조하고 싶을 때가 있는데, Perl에서는 영문, 숫자로 이루어진 이름 대신 실제 변수를 가리키는 레퍼런스를 반환하는 표현 방식으로 대치할 수 있다. 이것에 대해서는 4장, 레퍼런스와 중첩 데이터 구조에서 자세히 살펴보기로 한다.

스칼라 값

직접 혹은 간접적으로 참조되거나, 혹은 스택에 있는 임시 값을 나타내는 경우와 같이, 스칼라는 항상 하나의 값을 갖는다. 스칼라의 값은 숫자, 문자열, 혹은 다른 데이터를 가리키는 레퍼런스일 수도 있다.(스칼라에 값이 주어지지 않은 경우를 정의되지 않았다고 한다.) 스칼라가 어떤 값을 “갖고 있는” 경우에도, 실제로 스칼라의 타입이 정해져 있는 것이 아니다. 즉, Perl에서는 스칼라를 “숫자”나 “문자열” 타입으로 선언하는 방법은 없다. 즉, Perl에서는 필요에 따라 주어진 스칼라를 자동으로 변환하므로, 숫자를 문자열로, 혹은 문자열을 숫자로 취급하여 사용하면 주어진 상황에 맞게 자동으로 변환되어 적용된다.

문자열과 숫자가 주어진 상황에 따라 교환하여 사용 가능한 데 비해, 레퍼런스의 경우는 조금 다르다. 레퍼런스는 타입이 정해진, 타입 캐스팅(type casting)이 불가능한 포인터로서 기본적으로 reference-counting과 destructor invocation이 포함되어 있다. 이러한 레퍼런스는 사용자 정의의 객체를 만드는 등 복잡한 형태의 데이터 타입을 만드는 데 사용된다. 그러나 이런 레퍼런스 또한 스칼라의 하나로 볼 수 있다. 레퍼런스에 대해서는 4장에서 자세히 기술할 것이다.

숫자

Perl에서 숫자는 다음과 같은 몇 가지 형태로 구분할 수 있다.

12345 # 정수
12345.67 # 실수
6.02E23 # 지수 표기
0xffff # 16진수
0377 # 8진수
4_294_967_296 # 단위 구분을 위해 _을 사용

Perl에서 쉼표(,)는 리스트와 리스트를 구별하는 구분자로 사용되기 때문에 큰 숫자를 나타낼 때(예: 1,234,567) 단위 구분을 위해 쉼표를 사용하는 것이 불가능하다. 이와 같은 경우를 위해 쉼표 대신 Perl에서는 _을 사용하여 큰 숫자를 읽기 쉽도록 단위를 구분하는데 사용할 수 있다. 그러나 이런 _는 프로그램 내에서 숫자로 사용되는 경우에 한해서만 사용할 수 있으며, 표준 입력(STDIN)과 같이 프로그램 밖에서 읽어 들인 문자열을 숫자로 사용하는 경우에는 문자열과 숫자의 자동 변환이 적용되지 않는다. 마찬가지로 16진수를 나타내는 0x와 8진수를 나타내는 0의 경우도 숫자에서만 사용할 수 있다. 이러한 접두사는 문자열을 숫자로 자동 변환할 때 적용되지 않으므로 oct 함수 등을 사용하여 명확하게 변환 작업을 하여야 한다.

문자열

문자열은 홑 따옴표 또는 겹 따옴표로 둘러 쌓인 문자들을 말한다. UNIX 쉘에서와 마찬가지로 겹 따옴표 안에서는 역슬래쉬나 변수의 치환이 이루어지나 홑 따옴표에서는 그렇지 않다.(\’와 \\의 경우는 예외로서 홑 따옴표 안에 홑 따옴표나 역슬래쉬를 넣을 수 있다.)

또한 문자열 안에 개행문자를 포함할 수 있다. 즉 프로그램 내에서 문자열을 기술할 때 여러 줄에 걸쳐서 줄바꿈을 해가며 긴 문자열을 적을 수 있는 것이다. 이러한 것은 많은 경우 유용하게 쓰이나, 만약 문자열을 끝마치는 따옴표를 빠뜨릴 경우 해당 줄에서 오류가 발생할 수 있음을 주의해야 한다.

홑 따옴표 자신도 식별자(identifier, 변수나 파일 핸들 등에 붙는 이름)의 이름을 만드는 글자로 사용될 수 있으므로, 프로그램 내에서 홑 따옴표로 만들어진 문자열 앞에는 최소한 하나 이상의 공백 문자가 포함되어 있어야 이전 단어와 구분할 수 있다.(5장 참조)

겹 따옴표 문자열에서는 일반적인 C언어 스타일의 역슬래쉬 문자 치환이 비슷하게 적용된다. 줄바꿈과 탭, 8진수, 16진수 및 각종 제어 문자도 이에 해당된다.

코드
\n 줄바꿈
\r 캐리지 리턴
\t
\f 폼 피드
\b 백스페이스
\a 경고음(bell)
\e ESC 문자
33 ESC 문자(8진수 표기)
\x7f ESC 문자(16진수 표기)
\cC Control – C

이 밖에도 뒤에 오는 글자들을 변환하기 위해 사용되는 이스케이프 시퀀스에 다음과 같은 것들이 있다.

코드
\u 바로 다음 한 글자를 대문자로 변환
\l 바로 다음 한 글자를 소문자로 변환
\U 다음에 오는 모든 글자를 대문자로 변환
\L 다음에 오는 모든 글자를 소문자로 변환
\Q 다음에 오는 영문자, 숫자가 아니 모든 글자에 역슬래쉬
\E \U, \L, \Q의 마지막을 표시

위에서 역슬래쉬의 경우를 제외하고, 겹 따옴표 문자열에서는 스칼라나 리스트 변수의 변수 치환(variable interpolation)이 일어난다. 즉, 문자열 내에 특정한 변수의 값을 직접 포함시킬 수 있는 것이다. 이것은 실제로 문자열 연결(string concatenation)의 간편한 형태로 볼 수 있다. 변수 치환은 스칼라 변수, 배열, 배열이나 해쉬의 한 요소, 배열이나 해쉬의 한 부분에만 적용되며 해쉬 전체는 적용되지 않는다. 다시 말해서 변수 치환은 $나 @로 시작하는 변수에만 적용된다는 것이다. 비록 %로 시작되는 해쉬 전체는 문자열 내에서 치환되지 않더라도, 해쉬의 특정 값이나 일부는 $나 @로 시작되는 것으로 표시되므로 변수 치환이 가능하다.

다음 에제는 “The Price is $100.”을 출력한다.

$Price = ‘$100’; # 치환 안됨

print “The price is $Price.\n”; # 치환 됨

몇몇 쉘에서는 식별자와 그 뒤에 붙는 영숫자와 구별하기 위해 중괄호를 사용한다.(예: “How ${verb}able!” 실제로 중괄호안의 식별자는 문자열로 간주되며, 해쉬의 인덱스로 사용되는 식별자의 경우도 마찬가지이다. 예를 들어

$days{‘Feb’}

$days{Feb}

과 같이 쓸 수 있으며, 아래에서는 홑 따옴표가 있는 것으로 간주된다.

앞서와 같이 배열이나 해쉬 변수의 인덱스와는 별도로, 다단계의 치환은 이루어지지 않는다. 많은 쉘 프로그래머가 기대하는 것과는 반대로, 겹 따옴표 내에서의 역따옴표(`) 문자열이 치환되지 않으며, 홑 따옴표 안에서 변수 치환이 되지 않는다고 해서 겹 따옴표 안의 홑 따옴표 문자열 내의 변수가 치환이 안 되는 것은 아니다.

인용부호

일반적인 인용부호의 기능 – 문자열을 만드는 것 – 외에, Perl 에서는 다음과 같은 다양한 형태의 인용 부호를 갖춰 다양한 형태의 치환이나 패턴 일치 등 연산자로서의 기능을 제공한다.

보통 표기 일반 표기 치환
q// 문자열 No
“” qq// 문자열 Yes
  qx// 명령어 Yes
() qw// 단어 리스트 No
// m// 패턴 일치 Yes
s/// s/// 치환 대입 Yes
y/// tr/// 변환 No

치환이 되지 않는 인용부호 내부에서 특정한 글자들을 표기하기 위해 역슬래쉬를 많이 사용하지 않아도 된다. 영숫자나 공백이 아닌 임의의 글자를 /대신 사용할 수 있다. 만약 구분자가 홑 따옴표인 경우, 주어진 패턴에서 변수 치환은 일어나지 않는다. 만약 첫 구분자가 괄호나 대괄호, 중괄호, 삼각괄호 등이라면, 마지막 구분자도 같은 글자를 사용하여 짝이 맞아야 한다. 예를 들어,

$single = q!I said, “You said, ‘She said it.'”!;
$double = qq(Can’t we get some “good” $variable?);
$chunk_of_code = q {
    if ($condition) {
        print “Gotcha!”;
    }
};

마지막으로, 두 개의 문자열을 받는 s///나 tr///의 경우, 첫 번째 인용문의 구분자와 두 번째 것과는 같을 필요가 없다. 따라서 s{foo}(bar) 또는 tr[a-z][A-Z]와 같이 쓸 수 있다. 첫 번째와 두 번째 인용문 사이에는 공백 문자를 포함할 수 있으므로 다음과 같이 쓸 수도 있다.

tr [a-z]

[A-Z];

인용 부호를 생략하는 경우

다른 형태로의 해석이 이루어지지 않는 단어는 문자열로 취급된다. 이런 것들은 bareword(인용부호가 없는 순수한 단어: 역자)라고 한다. 예를 들어

@days = (Mon,Tue,Wed,Thu,Fri);

print STDOUT hello, ‘ ‘, world, “\n”;

의 경우 @days에는 월요일에서 금요일까지의 문자열이 대입되고, STDOUT으로는 hello world와 새줄문자가 출력된다. 만약 두 번째 줄에서 STDOUT과 같은 파일 핸들을 생략하게 되면, Perl은 hello를 파일 핸들로 간주하게 되고 이 경우 오류가 발생된다. 이런 경우 다양한 오류가 발생할 수 있으므로 bareword를 사용하지 않도록 미리 지정할 필요가 있다. 다음과 같이

use strict ‘subs’;

와 같이 할 경우 서브루틴 호출로 해석되지 않는 임의의 bareword는 컴파일 시 오류를 발생한다. 이러한 제한은 다음 블록의 마지막까지 계속 유효하며, 중간에 변경하고자 할 경우는 해당 부분에서

no strict ‘subs’;

와 같이 해 주면 된다.

다음과 같은 경우

“${verb}able”

$days{Feb}

중괄호 안의 verb나 Feb는 bareword가 아님을 주목하기 바란다. 이들은 다른 형태로의 해석이 이루어지지 않는 bareword의 법칙이 아니라, 앞 절에서와 같이 명확한 법칙에 의해 허용된 것이기 때문이다.

배열 값의 치환

배열 값은 join함수와 $”변수로 표시되는 구분자를 사용하여 겹 따옴표 문자열로 변환된다. 다음 둘은 같은 기능을 수행한다.

$temp = join($”,@ARGV);

print $temp;

print “@ARGV”;

패턴 일치에서 다음과 같은 혼동이 있을 수 있다. /$foo[bar]/는 /${foo}[bar]/(여기서 [bar]는 정규 표현식의 문자 클래스)로 해석될 것인지, 혹은 /${foo[bar]}/로 해석될 것인지(여기서 [bar]는 배열 @foo의 인덱스)? 만약 @foo가 존재하지 않는다면 [bar]는 확실히 문자 클래스임이 분명하다. 만약 @foo가 존재하면 Perl은 [bar]에 대해 추측을 하여 적절한 해석을 하게 된다. 대부분의 경우 Perl의 해석이 옳지만, 만약 그렇지 않은 경우에는 위와 같이 중괄호 {}를 사용하여 명확하게 지정하면 된다.

“Here” document

줄 구분 형태의 인용문은 쉘의 here-document문법에 근거한다. << 다음에 인용의 끝을 알리는 문자열을 적은 다음, 다음 번에 해당 문자열이 나오는 줄까지 모든 줄이 인용문이 되는 것이다. 인용을 구분 짓는 문자열로는 하나의 식별자 혹은 인용문이 올 수 있다. 만약 인용문을 사용하는 경우에는, 앞 절에서와 같이 사용된 인용부호에 따라 문자열을 처리하는 방식이 달라지게 된다. 인용부호를 사용하지 않은 식별자는 겹 따옴표를 사용한 것과 같다. <<와 식별자 사이에는 띄어쓰기가 올 수 없다.(만약 띄어쓰기가 포함되면 널 식별자로 간주되어 다음 첫번째 줄까지 인용문에 해당된다. – 다음 예제 중 첫번째 Hurrah! 예제를 참고하기 바란다.) 인용을 마치는 문자열은 문자열 자체로만 존재하여야 하며 인용부호나 공백 문자에 둘러 싸여 있으면 안된다.

print <<EOF; # 앞의 예와 같음
The price is $Price.
EOF

print <“EOF” # 위와 같음. 겹따옴표로 확실하게 인용문을 지정
The price is $Price.
EOF

print <<‘EOF’ # 홑 따옴표 인용
All things (e.g. a camel’s journey through
A needle’s eye) are possible, it’s true.
But picture how the camel feels, squeezed out
In one long bloody thread, from tail to snout.
— C.S. Lewis
EOF

print << x 10; # 다음 줄을 10번 출력
The camels are coming! Hurrah! Hurrah!

print <<“” x 10; # 위와 같은 경우.
The camels are coming! Hurrah! Hurrah!

print <<`EOC` # 명령어 실행
echo hi there
echo lo there
EOC

print <<“dromedary”, <<“camelid”; # 인용문의 중첩
I said bactrian.
dromedary
She said llama.
camelid

문장을 끝마치기 위해서는 끝에 ;를 붙이는 것을 잊으면 안된다. 다음과 같은 경우 첫 번째 줄에 ;를 빠뜨리면 잘못된 결과가 출력된다.

print <<ABC
179231
ABC
+ 20; # 179251을 출력

기타 문자열 토큰

현재 행 번호와 파일 명을 나타내는 __LINE__과 __FILE__은 각각 토큰으로만 사용되며 문자열로 치환되지는 않는다. 그밖에 __END__는 실제로 file의 끝 이전에 스크립트의 마지막을 알릴 때 사용되며 이후의 텍스트는 무시되나 DATA 파일 핸들을 통해 읽을 수는 있다.

__DATA__토큰은 __END__토큰과 비슷하게 동작하지만, 현제 패키지의 이름 영역 내에서 DATA파일 핸들을 open한다. (5장에서 좀 더 자세히 다룬다.)

구문

지금까지 Perl에서 스칼라 값을 만드는 몇몇 용어에 대해 알아보았다. 다른 용어에 대해 자세히 알아보기 전에, 구문(context)에 대해 짚고 넘어가기로 하자.

스칼라와 리스트 구문

Perl 스크립트 내에서의 모든 연산은 특정한 구문 내에서 평가되고, 해당 연산이 어떻게 수행되는지는 해당 구문의 요구조건에 의해 결정된다. 구문에는 크게 스칼라와 리스트 구문 두 가지가 있다. 예를 들어, 스칼라 변수에 값을 대입하는 경우 대입연산자 우변의 내용을 스칼라로 평가하고, 배열이나 해쉬(혹은 그 일부분)에 대입하는 경우는 우변을 리스트로 평가한다. 스칼라 값으로 이루어진 리스트로의 대입은 우변을 리스트로 평가한다.

몇몇 연산자는 자신이 속해 있는 구문에 맞는 반환값을 내놓는다. 리스트를 필요로 하는 구문에서는 리스트를, 스칼라를 원하는 구문에서는 스칼라를 반환한다.(여기에 해당하는 연산은 해당 연산을 설명할 때 언급하도록 하겠다.) 이러한 것을 컴퓨터 용어로는 반환값의 타입에 오버로드(overload)되었다고 말한다. 이것은 매우 단순한 형태의 오버로딩(overloading)으로서, 해당 구문에서 단지 하나의 값을 원하는 지, 혹은 둘 이상의 값을 원하는 지에 따라 결정되는 것이다.

그 밖의 연산자에게 리스트를 제공하는 다른 연산의 경우, 문법 설명에서는 LIST로 표시되어 있으므로 쉽게 구분할 수 있다. 필요한 경우, LIST의 중간에서 스칼라 함수를 사용하여 강제로 스칼라 값을 갖도록 지정할 수 있다. (반대로 스칼라 구문에서 강제로 리스트를 지정할 수는 없다.)

스칼라 구문은 좀 더 자세히 구분하자면 문자열, 숫자로 나눌 수 있다. 앞서 스칼라와 리스트로 구분했던 것과는 달리 이들은 연산 시 구분되지 않는다. 연산의 결과로 필요한 값을 낸 후 숫자를 문자열로, 문자열을 숫자로 변환하는 등의 작업은 Perl에게 맡기는 것이다. 일부 스칼라 구문은 반환값이 문자열이든 숫자든 상관없는 경우가 있는데, 이 경우 문자열과 숫자의 변환은 이루어지지 않는다.(예를 들어 특정 스칼라 값을 다른 변수에 대입하는 경우가 이에 해당한다. 이 때 새로운 변수는 단순히 이전 값과 같은 타입의 값을 취하며 타입의 변환 작업 등은 필요 없게 된다.)

불린(Boolean) 구문

스칼라 구문 중에 특별한 것으로 불린(Boolean) 구문이 있다. 불린 구문은 단지 그 결과가 참인지 거짓인지만을 판별하는 경우에 사용된다. Perl에서 참과 거짓은 다음과 같이 정의할 수 있다. 즉, 널(Null) 문자열이 아니거나 숫자 0(혹은 문자열 “0”)이 아니면 참이다. 그리고, 레퍼런스는 항상 참이다.

불린 구문은 다른 형태로의 변환이 이루어지지 않는다.

앞에서 널 문자열이 거짓으로 정의된다고 했지만, 실제로 널 문자열에도 두 가지 종류가 존재한다. 정의된 것과 정의되지 않은 널 문자열이 바로 그것이다. 불린 구문에서는 이들 둘을 구분하지 않는다. 정의되지 않은 널 스칼라는 프로그램 수행 도중에 오류가 발생했거나, 파일의 끝에 도달했을 때, 혹은 초기화되지 않은 변수나 배열의 요소를 참조하려고 했을 때와 같이 실제로 존재하지 않는 값인 경우 반환되는 값이다. 이러한 정의되지 않은 널 스칼라는 처음으로 사용되는 시점에 정의되지만, 이렇게 하기 전에 defined 연산자를 사용하여 해당 값이 정의되었는지 아닌지를 판별할 수 있다.(물론 defined의 반환값은 항상 정의되어 있으나, 그 값이 항상 참인 것은 아니다.)

Void 구문

또 다른 특별한 형태의 스칼라 구문은 void 구문이다. 이 구문은 반환되는 값이 무엇이든 상관없거나, 혹은 반환값을 필요로 하지 않기도 한다. 어떻게 동작하는 지의 관점에서는 void 구문이 다른 스칼라 구문과 다른 점이 없으나, 만약 Perl의 -w 스위치를 사용해 실행할 경우, 특정 값을 반환하지 않는 문장과 같은 부분에서 Perl은 다음과 같이 경고메시지를 보낸다.

“Camel Lot”; # 프로그램 내에서 다음과 같이 했을 경우

Useless use of a constant in void context in myprog line 123;

치환(Interpolative) 구문

앞서 언급한 것과 같이 겹 따옴표 문자열 내에서는 역슬래쉬 치환과 변수 치환이 이루어진다. 그러나 여기서 언급하는 치환 문은 이러한 겹 따옴표 문자열 외에도, 일반화된 명령어 입력 연산자 qx//나 패턴 일치 연산자 m//, 치환 연산자 s///로 만들어지는 구문도 포함된다. 실제로 치환 연산자에서는 패턴 일치 전에 좌변에 대해 치환을 먼저 한 다음, 매번 좌변이 일치할 때마다 우변에 대해 치환 작업을 하게 된다.

이러한 치환 구문은 인용부호 내에서, 혹은 인용부호와 비슷한 기능을 갖는 부호 내에서만 이루어지므로, 스칼라나 리스트와 같은 차원에서의 구문으로 분류하기에는 적당하지 않은 점이 있다.

리스트 값과 배열

지금까지 구문에 관해 얘기했으므로, 앞으로는 리스트 값에 대해서, 그리고 구문 내에서 리스트가 어떻게 동작하는 지에 관해 알아보기로 한다. 리스트 값은 쉼표로 구분되는 개별적인 값들로써 표시되며, 우선순위가 중요한 곳에서는 특별히 다음과 같이 괄호를 사용하여 표시한다.

(LIST)

리스트 내에서 리스트의 값은 리스트에 속해 있는 모든 값들을 순서대로 나열한 것에 해당된다. 반면 스칼라에서 리스트의 값은 C의 쉼표 연산자에서와 같이 맨 마지막 값(맨 우측의 값)에 해당된다. (앞서 얘기한 용어로 설명하자면, 쉼표 연산자의 좌측은 void 구문을 만들게 되는 것이다.) 예를 들어

@stuff = (“one”, “two”, “three”);

는 전체 리스트 값을 배열 @stuff 에 대입하지만,

$stuff = (“one”, “two”, “three”);

는 $stuff = “three”;와 동일한 효과를 갖는다. 쉼표 연산자는 자신이 스칼라 구문에 있는지, 리스트 구문내에 있는지를 파악하여 위와 같은 연산 결과를 낸다. 또한 배열 변수 자신도 자신이 속해 있는 구문을 알고 있다. 리스트 구문에서는 배열 변수 자신은 배열 전체 요소를 가리키지만, 스칼라 구문에서는 배열의 길이를 반환하게 된다. 아래 예에서 $stuff 의 값은 3이 된다.

@stuff = (“one”, “two”, “three”);

$stuff = @stuff; #$stuff 는 “three” 가 아닌, 3 을 갖는다

지금까지 LIST는 단순히 리스트 값의 나열로만 설명했으나, 실제로 임의의 값을 반환하는 모든 표현도 리스트 내에서 쓰일 수 있다. 이러한 값은 스칼라 값일 수도 있고 리스트 값일 수도 있다. LIST에서는 자동으로 서브리스트의 치환이 이루어진다. 즉, LIST 의 값이 평가될 때, 리스트의 각 요소는 리스트 구문에서 평가된 다음, 그 결과 만들어지는 리스트가 LIST로 치환되는 것이다. 따라서 배열은 LIST내에서는 자신의 독자성을 상실하게 된다. 다음와 같은 리스트

(@foo,@bar,&SomeSub)

는 @foo의 모든 요소에 @bar의 모든 요소, SomeSub 서브루틴의 반환값을 합한 모든 요소를 포함하게 된다. 만약 이러한 치환이 이루어지지 않게 하고 싶은 경우에는 배열로의 레퍼런스를 사용하면 되며, 이것은 4장에서 다룰 예정이다.

널 리스트는 ()로 표시한다. 리스트 내에서 널 리스트의 치환은 아무 효과도 없다. 즉, ((),(),())는 ()와 같다는 뜻이다. 이와 비슷하게 아무 요소도 갖고 있지 않는 배열의 치환 역시 아무 효과도 없다.

리스트의 마지막 값 다음에 추가로 쉼표를 붙일 수 있다. 이렇게 하면 나중에 리스트의 요소를 추가할 때 쉬워진다. 아래 예를 참고하기 바란다.

@numbers = ( 1, 2, 3, );

리스트를 표시하는 또 다른 방법으로는 앞서 언급한 것과 같이 qw(quote words)문장을 사용하는 것이다. 이 경우 공백문자로 구분된 홑 따옴표 문자열을 나열한 것과 같은 효과를 갖는다. 예를 들면,

@foo = qw( apple banana carambola coconut guava kumquat mandarin nectarine peach pear persimmon plum );

(위의 예에서 괄호는 일반적인 괄호가 아니라 인용 부호로 사용되었음을 주목하기 바란다. 따라서 괄호 대신에 삼각괄호나 중괄호, 슬래쉬 등을 괄호 대신 사용할 수도 있다.)

또한 리스트 값을 일반 배열처럼 인덱스를 사용하여 참조할 수도 있다. 이 경우 혼동을 피하기 위해 리스트를 괄호로 구분해 주어야 한다. 예를 들면,

# Stat은 LIST 값을 반환한다

$modification_time = (stat($file))[9];

# 다음과 같이 할 경우 문법 오류 발생

$modification_time = stat($file)[9]; # 괄호를 잊었군요

# 16진수 글자 찾기

$hexdigit = (‘a’,’b’,’c’,’d’,’e’,’f’)[$digit-10];

# reverse comma operator

return (pop(@foo),pop(@foo))[0];

리스트는 각각의 요소 단위로 값을 대입할 수 있다.

($a, $b, $c) = (1, 2, 3);

($map{red}, $map{green}, $map{blue}) = (0x00f, 0x0f0, 0xf00);

스칼라 구문에서의 리스트 대입은 대입식의 우변에 있는 식이 만들어 내는 요소의 개수를 반환한다.

$x = ( ($foo,$bar) = (7,7,7) ); # $x는 2가 아니라 3을 갖는다

$x = ( ($foo,$bar) = f() ); # $x는 f()의 반환카운트를 갖는다

대부분의 리스트 함수는 최종적으로 널 리스트를 반환하기 때문에, 널 리스트 대입을 불린(Boolean) 구문으로 처리하는 경우 0, 즉 거짓 값을 갖게 되므로 리스트 처리의 종료 여부를 쉽게 파악할 수 있게 된다. 리스트의 마지막 요소는 배열이나 해쉬가 올 수 있다.

($a, $b, @rest) = split;

my ($a, $b, %rest) = @arg_LIST;

실제로 리스트 내에 아무 곳에서나 배열이나 해쉬를 사용할 수 있지만, 제일 먼저 오는 배열이나 해쉬가 대입되는 값 모두를 포함하게 되므로 나머지 리스트 요소는 정의되지 않은 채 남게 된다. 이것은 local이나 my 키워드를 사용하여 해당 배열이 비어 있는 채로 초기화되도록 할 경우 유용하게 쓰일 수 있다.

배열 @days의 요소 개수는 다음과 같이 @days를 스칼라 구문에서 평가하면 알 수 있다.

@days + 0; # 암시적으로 @days 를 스칼라 구문으로 해석

scalar(@days) # 명시적으로 @days 를 스칼라 구문으로 해석

여기서 위와 같은 방법은 배열에서만 적용되며 일반적인 리스트에는 적용되지 않음을 주의해야 한다. 쉼표로 구분된 리스트 값을 스칼라 구문에서 평가하면 요소 개수가 아닌 리스트의 맨 마지막 값을 반환함을 주목하기 바란다.

@days의 스칼라 평가와 밀접한 관계를 갖고 있는 것이 $#days이다. 이것은 배열의 마지막 요소에 해당하는 인덱스, 또는 ‘배열의 길이 -1’을 반환한다. 배열의 인덱스는 0부터 시작하기 때문에) $#days에 값을 대입하면 배열의 길이가 달라진다. 이러한 방법으로 배열의 길이를 짧게 하면 중간에 끼인 값들이 없어지게 된다. 또한 이러한 방법으로 계속 크기가 커져 가는 배열의 크기를 미리 증가시키는 효과를 얻을 수 있다.(또한 배열의 마지막 값 다음의 요소에 값을 대입하여 배열을 늘릴 수도 있다.) 배열에 널 리스트 ()을 대입하여 배열을 없앨 수도 있다. 다음 두 문장은 동등하다.

@whatever = ();

$#whatever = -1;

그리고 다음은 항상 참이다.

scalar(@whatever) == $#whatever + 1;

해쉬 (Associative Array)

앞서 설명한 대로, 해쉬는 숫자로 된 인덱스가 아니라 키 문자열로써 그 값을 참조하는 특별한 형태의 배열을 말한다. 조합 배열이라는 말은 키와 그 키에 해당하는 값이 1:1로 결합되어 있는 것에서 비롯된 것이다.

실제로 Perl에서는 해쉬 구문에 속하는 것은 없으나, 보통의 리스트를 해쉬에 대입하면 각각의 쌍에 해당하는 값들이 해쉬의 키와 값으로 취해진다.

%map = (‘red’, 0x00f, ‘green’, 0x0f0, ‘blue’, 0xf00);

위 문장은 다음과 같은 효과를 갖는다.

%map = (); # 맨 먼저 해쉬를 비운다

$map{red} = 0x00f;

$map{green} = 0x0f0;

$map{blue} = 0xf00;

위와 같은 방법 외에 =>를 사용하여 각각의 키와 값을 보기 쉽도록 표기할 수도 있다. 사실 =>는 쉼표와 같은 것이지만, 쉼표에 비해 해쉬의 키와 값의 관계를 더 확실히 나타낼 수 있으며, 또한 =>왼쪽의 키를 홑 따옴표로 인용한 것과 같은 효과를 갖기 때문에, 매번 키의 전후에 홑 따옴표를 입력하지 않으면서 해쉬 변수를 초기화할 수 있어서 편리하다.

%map = ( red => 0x00f, green => 0x0f0, blue => 0xf00, );

다음과 같이 나중에 레코드로 사용될 익명(anonymous) 해쉬 레퍼런스를 초기화하는데도 편리하다.

$rec = { witch => ‘Mable the Merciless’, cat => ‘Fluffy the Ferocious’, date => ’10/31/1776′, };

혹은 다음과 같이 복잡한 형태의 함수를 호출하기 위해 call-by-named-parameter를 사용하는 데에도 편리하다.

$field = $query->radio_group( NAME => ‘group_name’, VALUES => [‘eenie’,’meenie’,’minie’], DEFAULT => ‘meenie’, LINEBREAK => ‘true’, LABELS => \%labels, );

리스트 구문 내에서는 해쉬 변수(%hash)를 사용할 수 있으며, 이 경우 모든 키/값을 치환하게 된다. 해쉬가 특별한 순서대로 초기화되었다고 해서 해쉬의 값이 그러한 순서대로 나오는 것은 아니다. 해쉬는 검색시 빠른 속도를 위해 내부적으로 해쉬 테이블을 이용하는데, 실제로 해쉬 내부에 저장되는 순서는 해쉬 함수의 본래 특성에 의한 것이다. 따라서 실제로 해쉬의 출력값은 우리가 보기에 이상한 순서대로 나올 수도 있는 것이다.(물론 어떤 경우에도 키와 값의 쌍은 순서대로 출력된다.) 실제로 출력되는 순서를 어떻게 정렬하는 지에 관해서는 2장의 keys, 혹은 7장의 DB_File설명 중에서 DB_BTREE 부분을 참고하기 바란다.

해쉬 변수를 스칼라 구문 내에서 평가하는 경우, 해쉬 변수가 임의의 키/값 한 쌍에 대한 값을 갖고 있으면 참을 반환하고 그렇지 않으면 (임의의 키/값에 대해 하나도 정의가 되어 있지 않으면) 거짓을 반환한다.

타입글로브(Typeglob)와 파일 핸들

Perl에서는 내부적으로 전체 심볼 테이블 내역을 저장하기 위해 타입글로브(typeglob) 라는 타입을 사용한다. 타입글로브를 나타내는 표시는 *이다. 이 타입은 이전 버전의 Perl에서 함수에 배열이나 해쉬를 레퍼런스에 의해 전달(call-by-reference) 할 경우에 많이 사용되었으나, 현재는 실 레퍼런스(real reference) 타입이 있기 때문에 자주 사용되지는 않는다.

그러나 지금도 타입글로브는 파일 핸들을 전달하거나 저장할 때 사용된다. 만약 파일 핸들을 저장하고자 할 경우 다음과 같이 하거나

$fh = *STDOUT;

혹은 실 레퍼런스로 저장하려면,

$fh = \*STDOUT;

과 같이 한다. 또한 타입글로브는 지역(local) 파일 핸들을 만들 때도 사용된다. 예를 들면,

sub newopen {
    my $path = shift;
    local *FH;                                        # my 아니라 local임에 주의!
    open (FH, $path) || return undef;
    return *FH;
}

$fh = newopen(‘/etc/passwd’);

어떻게 파일 핸들을 생성하는 가에 대해서는 3장의 open이나 7장의 파일핸들(FileHandle) 모듈 부분을 참고하기 바란다.

그러나 앞서 말한 것 외에 타입글로브의 가장 중요한 용도는 심볼 테이블 내역을 다른 심볼 테이블 내역으로 전달할 때 사용하는 것이다. 예를 들어,

*foo = *bar;

와 같이 할 경우, 변수의 타입에 관계없이 “foo”는 “bar”로 이름 붙여진 모든 것과 동일한 효과를 갖는다. 또한, 타입글로브 대신 레퍼런스를 대입하여 한 타입의 변수만을 다른 변수로 전달할 수도 있다.

*foo = \$bar

위의 경우 $foo는 $bar와 같지만, @foo와 @bar, %foo와 %bar는 같지 않다. 이렇게 변수를 다른 변수로 앨리어스(alias)하는 것이 불필요하다고 생각할지 모르겠지만, 나중에 나올 모듈의 export/import 메커니즘은 이러한 타입글로브의 앨리어스 기능을 이용한 것이다. 4장과 5장에서 타입글로브에 관해 좀 더 자세히 알아보도록 하겠다.

 

입력 연산자

여기서 다룰 몇몇 입력 연산자는 의사 문자(pseudo-literal)라고도 부르는데, 그 이유는 여러 측면에서 마치 인용된 문자열과 비슷하게 동작하기 때문이다. (print와 같은 출력 연산자는 리스트 연산자로 동작하며 3장에서 다룬다.)

명령어 입력(backtick) 연산자

가장 먼저 백틱(`) 연산자로 알려져 있는 명령어 입력 연산자에 대해 알아보도록 하자. 명령어 입력 연산자는 다음과 같은 형태로 사용된다.

$info = `finger $user`;

(`)으로 둘러 싸인 문자열 내에서는 겹 따옴표 문자열에서와 마찬가지로 먼저 변수 치환이 이루어진다. 그 결과는 쉘에서의 명령어 수행으로 해석되어 실행된 다음, 해당 명령의 결과가 반환되어 돌아온다.(이것은 UNIX 쉘의 방식을 따른 것이다.) 스칼라 구문에서는 전체 출력 결과가 하나의 문자열이 되어 반환되고, 리스트 구문에서는 출력 결과의 각 줄별로 구분되는 리스트 값이 반환된다.($/ 변수의 값을 바꾸어 줄 구분자를 변경할 수 있다.)

주어진 명령어는 해당 의사 문자가 평가될 때마다 실행된다. 명령어 수행의 결과를 나타내는 숫자 값은 $?변수에 저장된다.(이 장의 뒷부분에 있는 “특수 변수” 부분에서 $?의 해석에 관한 내용을 참고하기 바란다.) csh 버전에서의 명령어와는 달리, 반환되는 데이터에 대한 변환은 이루어지지 않는다. 즉, 개행(new line) 문자는 그대로 개행문자로 남아 있게 된다. 다른 쉘에서와는 달리, 명령어 안에 있는 변수명을 해석되지 않도록 숨기지 않는다. 따라서 쉘로 $문자를 전달하려면 역슬래쉬를 사용해야 한다. 위의 예에서 $user는 Perl에 의해 치환된 것이지 쉘에 의해 치환된 것은 아니다.

(`)의 일반화된 형태는 qx// (quoted execution의 머릿글자) 이다.

라인 입력(앵글) 연산자

Perl 프로그램에서 가장 많이 사용되는 입력 연산자는 아마 앵글 연산자 < >로도 부르는 행 입력 연산자일 것이다. <STDIN>과 같이 앵글 연산자 < >안에 파일 핸들을 적으면, 해당 파일에서 다음 한 줄을 읽어오는 것을 의미한다.(개행 문자도 포함된다. 따라서 Perl의 참/거짓 측면에서 볼 때, 아무 내용이 없는 줄은 참이며, 파일의 끝도 역시 참이고, 이후 정의되지 않은 값이 반환되면 거짓이 된다.) 보통 입력된 값을 다른 변수로 대입하는 경우 자동 대입이 이루어지는 경우가 있다. 행 입력 연산자가 while 루프의 조건 판단 부분에 있을 경우, 입력된 값은 자동으로 특수 변수인 $_에 대입된다. 그 후 대입된 값은 그 값이 정의된 값인지 테스트된다.(아마 처음에는 이러한 것이 좀 이상하게 생각될지도 모르지만, 앞으로 만들 대부분의 Perl 스크립트는 이러한 방식으로 작성될 것이다.) 아래 예는 모두 같은 기능을 한다.

while (defined($_ = <STDIN>)) {print $_;} # 길게 쓰는 법
while (<STDIN>) {print;} # 짧게 쓰는 법
for (;<STDIN>;) {print;} # for로 while 루프 흉내내기
print $_ while defined($_ = <STDIN>); # 긴 문장 변환자
print while <STDIN>; # 짧은 문장 변환자

위와 같은 기능은 while 루프에서만 동작함을 기억하기 바란다. 만약 입력 연산자를 while 루프가 아닌 다른 곳에서 사용할 경우 입력된 값을 갖고 있으려면 반드시 다른 변수에 대입해 두어야 한다.

if (<STDIN>) {print; } # 틀림! 이전 $_값을 출력
if ($_ = <STDIN>) {print: } # 맞음.

파일 핸들 STDIN, STDOUT, STDERR는 미리 정의되고 열려진 것이다. 그 밖의 파일 핸들은 open 함수에 의해 만들어진다. open 함수에 대해서는 3장에서 자세히 알아보도록 하겠다. 몇몇 객체 모듈 또한 파일 핸들로 사용될 수 있는 객체 레퍼런스를 만들기도 한다. 여기에 대해서는 7장의 파일 핸들(FileHandle) 모듈을 참고하기 바란다.

위의 while 루프에서는 라인 입력 연산자의 결과를 스칼라로 평가하였기 때문에 반환값은 줄별로 구분되었다. 그러나 라인 입력 연산자를 리스트 구문에서 사용하게 되면, 나머지 모든 행을 포함하는 내용이 한 줄이 한 리스트 요소로 이루어지는 리스트 구문으로 된다. 따라서 아래 예와 같이 큰 데이터 영역을 만드는 것이 아주 쉽게 된다.

$one_line = <MYFILE>; # 첫 줄 읽어오기
@all_lines = <MYFILE>; # 나머지 모든 줄 읽어오기

while 루프의 조건 판단 부분에서는 항상 스칼라로 평가되기 때문에 while 루프에서는 위와 같은 리스트 입력은 이루어지지 않는다.

< > 연산자 안에 널 파일 핸들을 사용하여 일반적인 UNIX 필터 프로그램인 sedawk 등에서와 같은 명령어 행 특성을 흉내낼 수 있다. < > 에 의해 입력을 받는 경우, 명령어 행에 언급된 파일로부터 입력을 받게 된다. 만약 아무 파일도 지정하지 않은 경우에는 표준 입력(STDIN)으로부터 입력을 받게 되므로 파이프(|) 등을 사용하는 필터 프로그램을 만들기가 쉬워진다.

아래에 널 파일 핸들의 동작 예를 보였다. 첫 번째로 < >가 평가될 때, 배열 @ARGV이 체크된다. 만약 그 값이 널이면 $ARGV[0]은 “-“로 지정되어 표준 입력을 나타낸다. 그 다음 배열 @ARGV를 파일의 이름으로 처리한다. 즉 다음 루프는

while (<>) {
                                # 필요한 코드
}

아래와 같은 Perl 형태의 의사 코드와 

@ARGV = (‘-‘) unless @ARGV;
while ($ARGV = shift) {
    open(ARGV, $ARGV) or warn “Can’t open $ARGV: $!\n”;
    while (<ARGV>) {
                   # 실제 필요한 코드
    }
}

동일한 기능을 한다는 뜻이다. 실제로 배열 @ARGV를 shift하고, 현재 파일명을 변수 $ARGV에 대입한다. 또한 파일 핸들 ARGV를 내부적으로 사용한다. < >는 결국 <ARGV>와 같이 동작한다는 뜻이다.

실제로 필요한 파일명을 담고 있는 한, 첫 번째 < > 연산자가 나오기 전에 배열 @ARGV를 변경할 수도 있다. < > 연산자에 의해 읽어 들이고 있는 파일의 현재 줄은 $.변수에 의해 알 수 있다.(곧 이어 나올 eof 부분의 예에서 이러한 현재 줄 번호를 어떻게 하면 초기화할 수 있는 지를 살펴보기 바란다.)

@ARGV가 파일명들을 나타내는 경우는 괜찮지만, 스위치나 옵션 등을 나타내는 경우라면 Getops 모듈을 사용하든지 아니면 다음과 같은 루프를 프로그램 맨 앞에 삽입해야 할 것이다.

while ($_ = $ARGV[0], /^-/) {
        shift;
        last if /^–$/;
        if (/^-D(.*)/)        { $debug = $1 }
        if (/^-v/) { $verbose++ }
       
}

while (<>) {
                                # 필요한 코드
}

파일의 끝에 도달해서 < >는 단 한번 거짓을 반환하게 된다. 만약 그 후로 계속 < >을 사용하게 되면, 또 다른 @ARGV를 처리하는 것으로 알게 되며, 만약 @ARGV를 지정하지 않은 상태에서는 STDIN으로 입력을 받게 된다.

만약 <>연산자 안에 스칼라 변수가 있는 경우(예를 들어 <$foo>), 해당 변수는 읽어 들일 파일 핸들의 이름이나 레퍼런스를 갖고 있어야 한다. 예를 들면,

$fh = \*STDIN;
$line <$fh>;

와 같이 해야 한다.

파일명 글로빙(globbing) 연산자

만약 라인 입력 연산자 < >안에 다른 무엇인가를 포함하게 되면 어떻게 동작할까? 만약 < >연산자 내에 파일 핸들이나 스칼라 변수 외의 다른 것이 포함되어 있을 경우, 그것은 글로브(glob) 될 파일명의 패턴으로 해석된다. 현재 디렉토리 (혹은 글로브 패턴의 일부로서 지정된 디렉토리)에서 해당되는 패턴에 일치하는 파일명이 이 연산자에 의해 반환된다. 라인 입력 연산자와 마찬가지로, 일치된 파일명은 스칼라 구문에서는 한 번에 하나씩 반환되고, 리스트 구문에서는 한꺼번에 반환된다. 실제로는 뒤쪽의 방법이 많이 쓰인다. 실제로 프로그램 내에서 다음과 같은 줄을 쉽게 볼 수 있을 것이다.

my @files = <*.html>;

다른 종류의 의사 문자에서와 마찬가지로 한 단계의 변수 치환이 이루어지기는 하지만, <$foo>와 같이는 사용할 수 없다. 왜냐하면 이것은 앞서 언급한 간접 파일 핸들이기 때문이다.(이전 버전의 Perl에서는 파일명의 글로브를 강제로 지정하기 위해 중괄호 {}를 사용해야만 했다: <${foo}> 현재 버전에서는 굳이 이렇게 할 필요 없이 내부 함수 glob($foo)를 호출하면 된다.)

glob 함수를 사용하든지 혹은 < >를 사용하든간에, 글로빙 연산자는 while 루프에서 라인 입력 연산자와 마찬가지의 기능을 가지며, 입력된 결과를 $_에 저장한다. 예를 들어,

while (<*.c>) {
        chmod 0644, $_;
}

는 다음과 같은 기능을 한다.

open(FOO, “echo *.c | tr -s \t\r\f’ ‘\12\12\12\12’|”);
while (<FOO>) {
    chop;
    chmod 0644, $_;
}

물론 위와 같은 작업을 하는 가장 간단한 형태의 문장은 다음과 같다.

chmod 0644, <*.c>;

그러나 위와 같은 글로빙은 서브 쉘을 호출하므로, 경우에 따라서는 직접 readdir을 호출하고 파일명에서 필요한 것을 grep하는 것이 빠를 수 있다. 또한, 쉘을 이용한 글로빙 구현에 따르면, 가끔 “Arg list too long” 오류 메시지가 나오기도 한다.

글로빙에서는 피연산자를 새 리스트가 시작될 때 한 번 평가한다. 따라서 모든 값이 다음으로 진행되기 전에 읽어져야만 한다. 리스트에서는 자동으로 모든 값을 읽어 오기 때문에 문제가 되지 않지만, 스칼라에서는 매번 호출할 때마다 다음 번 값을 반환하다가, 맨 마지막에 값을 전부 반환한 다음에 거짓을 반환하기 때문에 문제가 발생할 수 있다. 따라서, 글로빙한 결과값이 하나일 경우라면 다음과 같이 표현하는 것이

($file) = <blurch*>; # 리스트 구문

다음보다 낫다.

$file = <blurch*>; # 스칼라 구문

왜냐하면, 앞의 예는 blurch에 일치하는 파일명을 한꺼번에 반환하면서 연산자를 초기화하지만, 뒤의 예에서는 일치하는 파일명을 반환하면서 동시에 거짓을 반환하기 때문이다.

만약 변수 치환을 할 경우라면 이전 버전에서의 표기법보다 glob연산자를 사용하기 바란다. 이전 버전에서의 표기는 사람들로 하여금 간접 파일 핸들 표기와 혼동을 가져올 수 있기 때문이다. 다음과 같이 glob를 사용하면 연산자와 기타 용어간의 경계가 명확해지기 때문에 그 같은 혼동을 피할 수 있다.

@files = glob(“$dir/*.[ch]”); # glob를 함수로서 호출

@files = glob $some_pattern; # glob를 연산자로서 호출

두 번째 예에서 glob의 괄호를 생략하였는데, 그것은 glob이 인수를 하나 취하는 1원 연산자로 사용될 수 있다는 것을 나타낸 것이다. glob연산자는 1원 연산자(named unary operator)의 한 예로서, 이 장의 뒷부분에 나올 “연산자” 절에서 다룰 것이다. 그러나 우선 여기서는 용어를 분석하면서 연산자처럼 동작하는 패턴 일치 연산자에 대해 먼저 알아보도록 하자.

패턴 일치

Perl에서 패턴 연산자는 일치 연산자 m//와 치환 연산자 s///이 있다. 이밖에 첫 번째 인수를 일치 연산자로 취하고 그 밖에는 함수와 비슷하게 동작하는 split 연산자가 있으나 이에 대해서는 3장에서 자세히 다룰 것이다.

비록 여기서는 m//과 s///와 같이 나타냈지만, 필요에 의해 /로 나타낸 구분자는 다른 글자를 사용할 수 있다는 것은 이전에 언급한 바가 있다. 반면 m//연산자에서는 구분자가 슬래쉬인 경우에 한해 m을 생략할 수 있다.

이러한 구분자나 인용 부호 안에는 정규 표현식(Regular Expression)이라고 불리우는 내용이 포함되게 되는데, 여기에 대해서는 다음 절에서 자세히 다룰 것이다.

일치 연산에는 다양한 변환자가 있는데, 그 중에서 정규 표현식의 해석에 영향을 끼치는 몇 가지를 들면 다음과 같다.

변환자
l 영문 대소문자 구별없이 패턴 일치
m 문자열을 여러 줄로 취급(^와 $는 \n과 일치)
s 문자열을 한 줄로 취급(^와 $는 \n을 무시, 그러나 .는 \n과 일치)
x 공백문자와 주석문으 이용해 패턴의 가독성을 늘릴 때 사용

실제로 이들 변환자는 “/x 변환자” 등으로 표기되지만, 앞서 언급한 대로 구분자가 반드시 슬래쉬일 필요는 없다. 실제로 이들 구분자는 정규 표현식 내에 (? … ) 구조를 사용하여 포함되기도 한다. 이 장의 “정규 표현식 확장” 절을 참조하기 바란다.

/x변환자에 대해서는 조금 더 설명할 필요가 있을 것 같다. /x변환자는 정규 표현식을 분석할 때 역슬래쉬가 붙지 않은 공백문자나 문자 클래스 내에 들어있는 공백 문자를 무시하도록 한다. 따라서 /x변환자를 사용해서 정규 표현식을 좀 더 읽기 쉽도록 부분으로 나눌 수 있게 된다. 또한 #도 보통 Perl 코드 내에서와 같이 주석문을 나타내는 글자로 취급된다. 이들을 함께 사용하여 Perl 코드를 좀 더 읽기 쉽도록 만들 수 있다.

정규 표현식

패턴 일치나 치환 연산자에서 사용되는 정규 표현식은 UNIX의 egrep프로그램 등에서 사용되는 것과 문법적으로 유사하다. 정규 표현식을 위한 문법에 맞도록 작성한 다음, 정규 표현식 해석기(이것을 엔진이라고 부른다)가 이것을 해석하여, 주어진 문자열에서 해당 정규 표현식의 패턴이 나타나면 “yes”, 그렇지 않으면 “no”라고 판단하는 것이다.

엔진이 “yes”라고 판단한 다음 어떤 일이 일어나는지는 어느 곳에서 어떻게 정규 표현식을 사용했느냐에 따라 달라진다. 만약 조건문으로 패턴 일치를 사용한 경우, 패턴이 일치된 곳이 어디인지가 중요한 것이 아니라, 패턴이 일치했는지 아닌지를 살펴보는 것이 더 중요할 것이다.(물론 필요한 경우에는 조건문에서 사용했더라도 어디에서 패턴 일치가 일어났는지 알아낼 수 있다.) 치환 명령을 사용했을 때는 패턴이 일치된 부분을 지정된 문자열로 대치하게 할 수 있다. 또한 split 연산자를 사용하여 주어진 패턴으로 문자열을 분해, 리스트로 변환하도록 할 수도 있다.

정규 표현식은 매우 강력할 뿐 아니라, 작은 공간에 상당히 많은 의미를 포함할 수 있다. 따라서 긴 정규 표현식의 의미를 한 번에 직관적으로 이해하는 것은 어려울 지도 모른다. 그러나 정규 표현식을 잘게 나눈 다음, 엔진이 그들 부분을 어떻게 해석하는 지를 안다면, 어떤 정규 표현식도 쉽게 이해할 수 있을 것이라 생각한다.

정규 표현식에 관한 몇 가지

정규 표현식을 해석하는 법칙에 대해 깊이 들어가기 전에 먼저 정규 표현식에서 자주 보게 될 몇 가지 것들에 대해 살펴보기로 하자. 먼저 문자열에 대해 알아보자. 정규 표현식 내의 대부분의 글자는 단순히 해당 글자와의 일치 여부를 판단하게 된다. 만약 몇 개의 글자를 연이어 놓으면, 해당하는 글자가 순서대로 일치해야 한다. 따라서 만약 패턴 일치를 다음과 같이 했다면,

/Fred/

문자열 내에 “Fred”가 포함되어야만 패턴이 일치할 것이다.

그 밖에 다른 몇몇 글자들은 그들 자신의 일치 여부를 판별하는 데 사용되는 것이 아니라, 메타 문자(meta-character)로 사용된다.(메타 문자가 무슨 일을 하는 지는 나중에 설명하도록 한다. 이러한 메타 문자 자신의 일치 여부를 판별하기 위해서는 해당 글자 앞에 역슬래쉬를 놓으면 된다. 예를 들어, 글자로서의 역슬래쉬의 일치 여부를 판별하려면, 역슬래쉬 앞에 역슬래쉬를 놓으면 된다: \\) 메타 글자로는 다음과 같은 것들이 있다.

\ | ( ) [ { ^ $ * + ? .

앞서 메타 문자를 일반 문자와 같이 취급하려면 앞에 역슬래쉬를 붙이면 된다고 했는데, 영문자나 숫자의 경우는 이와 반대이다. 즉, 영문자나 숫자 앞에 역슬래쉬를 붙이면 메타 글자 혹은 메타 시퀀스로 바뀐다. 예를 들어 다음과 같은 시퀀스에서

\b \D \t \3 \s

\b는 단어의 경계, \t는 탭 문자와 일치한다. 여기서 단어의 경계는 글자 폭이 0이고, 탭 문자는 글자 폭이 1임을 주목하기 바란다. 하지만 이들 둘은 문자열 내의 특정한 지점에 대해 무엇인가를 판별한다는 점에서는 비슷하다. 정규 표현식에서 대부분의 것들은 자기 자신의 일치 여부를 판별하는 보통의 문자를 포함해서 이러한 일치 여부에 대한 것을 구분해 놓은 것이다.(좀 더 정확하게 말하자면, 메타 문자나 메타 시퀀스 중에는 문자열에서 다음에 올 문자의 일치 여부까지 해당되는 것이 있다. 앞서 탭 문자의 글자 폭이 1이라고 말했던 것도 이런 뜻에서이다. 어떤 경우에는 가능한 한 가장 많이 일치하도록 하는 것이 있는 반면, 그렇지 않은 것도 있다. 일치 여부를 나타내는 길이가 0이 아닌 단위를 아톰(atom)이라고 한다.) 이러한 일치 여부와 관계없는 것들도 있다. 예를 들면 수직 바(|)로 표시되는 교대 문자열(alternation)을 들 수 있다.

/Fred|Wilma|Barney|Betty/

위와 같은 경우 4개의 문자열 중 하나만 일치하면 된다. 다양한 종류의 것들을 하나로 묶기 위해서는 괄호를 사용하며, 하나의 긴 정규 표현식에서 몇 개의 문자열을 교대로 나타내기 위해서는 다음과 같이 한다.

/(Fred|Wilma|Pebbles) Flintstone/

이 밖에 수량자(quantifier)라는 것이 있다. 이것은 바로 앞의 것이 얼마나 많이 있는 지를 나타낼 때 사용한다. 이러한 수량자는 다음과 같은 것들이 있다.

* + ? *? {2,5}

이러한 수량자는 아톰 뒤에 붙었을 때만 그 의미가 있다. 즉, 길이가 1이상이어야만 한다. 또한, 수량자는 바로 앞의 아톰에만 적용된다. 쉽게 말해 바로 앞의 한 문자에만 적용된다는 것이다. 예를 들어 “moo”를 연속해서 세 번 일치하고 싶으면 다음과 같이 “moo”를 괄호로 묶어 주어야 한다.

/(moo){3}/

위 정규 표현식은 “moomoomoo”와 일치한다. 만약 위와 같이 하지 않고 /moo{3}/과 같이 하면 “moooo”와 일치하게 된다.

패턴은 겹 따옴표 문자열로 처리되므로, 일반적인 겹 따옴표 문자열 내에서의 치환이 그대로 적용된다. 이러한 치환은 문자열이 정규 표현식으로 해석되기 이전에 이루어진다. 여기서 주의할 점은, $다음의 |, 닫는 괄호 ), 혹은 문자열의 마지막은 변수의 치환이 아니라 행의 마지막으로 해석된다. 따라서 다음과 같이 할 경우,

$foo = “moo”;

/$foo$/;

이것은 다음과 같다.

/moo$/

또한 패턴 내에서의 변수의 치환은, 매번 패턴 일치시마다 패턴에 포함되어 있는 변수의 값이 바뀌었을 지도 모르기 때문에 주어진 패턴을 재컴파일하는 과정을 겪는다. 이 경우 패턴 일치 작업을 느리게 만든다는 점을 주의해야 한다.

정규 표현식 일치의 법칙

지금부터는 정규 표현식 해석기(엔진)가 주어진 문자열에 대해 패턴의 일치 여부를 판별하는 법칙에 대해 살펴보도록 하겠다. Perl의 엔진은 NFA(nondeterministic finite-state automation)이라고 하는 방법을 사용하여 패턴의 일치를 판별한다. 쉽게 말하자면, 주어진 문자열에 대해 패턴의 일치 여부를 판별하기 위해서 어떤 방법을 적용했고, 어떤 방법을 아직 적용하지 않았는지를 기억하고 있다가 만약 해당 문자열의 끝까지 도달하도록 패턴의 일치가 이루어지지 않았을 때는 앞서의 기억을 토대로 되돌아와서 다른 방법을 적용해서 패턴 일치 여부를 재판별한다는 뜻이다. 이러한 방법을 일컬어 백 트래킹(backtracking)이라고 한다. Perl 엔진은 주어진 문자열의 한 지점에서 가능한 모든 경우에 대해 패턴의 일치를 조사하고, 이 모든 경우에 대해서도 일치하지 않으면 되돌아와서 다른 지점에서 모든 경우에 대한 패턴 일치 여부를 재조사한다. 따라서 이렇게 많은 시간이 소요되는 백 트래킹을 피하려면 일치 여부를 판별하기 위한 패턴을 효율적으로 작성해야 할 것이다.

아래에 열거한 패턴 일치의 법칙의 순서는 실제로 엔진이 패턴 일치 판별시 적용되는 순서를 말한다. 따라서 “left-most, longest match”와 같은 문구는, 실제로 Perl에서는 길게 일치하는 것(longest match)보다는 가능한 한 왼쪽에서 일치(left-most match)하는 것이 우선 순위가 더 높다는 것을 의미한다.

법칙 1 전체 정규 표현식이 법칙 2하에서 일치하는 경우, 가능한 한 주어진 문자열의 왼쪽에서 일치 여부를 판별한다.

실제로 법칙 1을 만족하는 첫 번째 방법은, 주어진 문자열의 첫 번째 글자 직전에서 시작하여, 그 지점에서 전체 정규 표현식이 일치하는 지를 판별하는 것이다. 주어진 문자열의 끝에 도달하기 전에 엔진이 정규 표현식의 끝까지 도달하여 일치하면 전체 정규 표현식이 일치하게 된다. 만약 일치하게 되면, 그 즉시 패턴 일치는 종료된다. 즉, 다른 여러 가지 방법으로 주어진 문자열 내에서 일치할 수 있더라도, “더 좋은” 일치 방법을 찾지 않는다는 뜻이다. 이러한 패턴 일치는 주어진 정규 표현식의 끝까지만 일치하면 되며, 반드시 주어진 문자열의 끝까지 도달해서 일치할 필요는 없다.(단, 정규 표현식 내에서 문자열의 끝을 나타내는 \$등의 위치 문자(assertion)가 오는 경우는 예외이다.) 첫 번째 지점에서 가능한 모든 방법을 동원해도 일치하지 않으면, 두 번째 지점으로 옮겨서 같은 작업을 반복한다. 만약 이번에 일치하게 되면, 거기서 패턴 일치를 종료한다. 그렇지 않고 실패하게 되면, 다음 지점으로 옮겨 일치 여부를 다시 판별한다. 주어진 문자열의 마지막 글자 다음 위치까지 포함하여 모든 지점에서 전체 정규 표현식이 일치하지 않는다면, 패턴 일치는 실패한 것이다.

주어진 문자열을 구성하고 있는 글자 사이에서 일치가 일어남을 주목해야 한다. 이렇게 말하면 0개 이상의 x와의 일치를 나타내는 /x*/와 같은 패턴을 사용하는 것에 대해 의아해 할 지도 모른다. 예를 들어 “fox”와 같은 문자열에서 패턴을 찾는 경우, 실제로는 나중에 나오게 될 “x”에 앞서 “f”바로 앞의 널 문자열에서 이미 주어진 패턴 /x*/는 일치하게 된다.(0개의 x 와 일치했으므로) 1개 이상의 x와 일치여부를 판단하려 하는 경우라면 /x+/를 사용해야 한다. 법칙 5의 수량자에 관한 부분을 참고하기 바란다.

이 법칙에 의하면 다음 법칙도 성립한다. 즉, 널 문자열과 일치하는 정규 표현식은 주어진 문자열의 가장 왼쪽 위치에서 반드시 일치한다.

법칙 2 전체 정규 표현식은 교대 문자열의 집합으로 간주할 수 있다. (가장 간단한 형태는 하나의 교대 문자열을 갖는 정규 표현식이다.) 만약 둘, 혹은 그 이상의 교대 문자열은 |에 의해 구분된다. 교대 문자열의 집합은 문자열을 구성하고 있는 교대 문자열 중 어느 것이라도 법칙 3하에서 주어진 문자열과 일치할 경우 일치하게 된다. 교대 문자열 집합에서는 정규 표현식에서의 위치로 보았을 때 왼쪽에 있는 교대 문자열부터 오른쪽으로 차례대로 일치 여부를 판별하며, 전체 정규 표현식이 일치하는 가장 첫 번째 교대 문자열에서 패턴 일치 판별이 종료된다. 만약 교대 문자열 중 어느 것도 일치하지 않으면, 법칙 2를 호출한 법칙(일반적으로 법칙 1, 그러나 경우에 따라 법칙 4 혹은 법칙 6이 될 수 있다.)으로 되돌아가서 주어진 문자열의 새로운 위치에서 법칙 2를 적용해 보게 된다.

법칙 3 특정한 교대 문자열은 해당 교대 문자열 내의 모든 아이템이 순서대로 법칙 4와 법칙 5에 따라 일치하게 되는 경우 일치한다고 할 수 있다. 교대 문자열 내의 아이템으로는 법칙 4에서 설명하게 될 위치 문자(assertion)나 수량자로 지정된 아톰(quantified atom)등이 해당된다. 아이템은 순서대로 왼쪽에서 오른쪽으로 일치하여야 하며, 만일 그렇지 못할 경우 엔진은 법칙 2하에서 다음 번 교대 문자열로 넘어간다.

아이템이 순서대로 일치해야 한다는 것이, 정규 표현식 내에서 해당 아이템들이 특별한 구분자로 구분되어 있다는 뜻은 아니다. 단지 그 순서대로 일치하여야 한다는 뜻이다. 예를 들어 /^foo/와 같은 패턴의 일치를 판별할 때, 하나씩 순서대로 4개의 아이템이 일치해야 한다는 뜻이다. 제일 먼저 줄의 처음을 나타내는 0글자 폭의 위치 문자 ^, 그리고 “f”, “o”, “o”라는 3개의 알파벳이 순서대로 일치해야 한다는 것이다.

왼쪽에서 오른쪽으로 “순서에 맞게” 일치한다는 것은 다음과 같은 패턴에서

/x*y*/

x가 일치하는 한 가지 경우에 대해 y는 가능한 모든 경우의 일치 여부를 판별한다. 만일 이것이 실패하면, x가 일치하는 다음 경우에 대해 y도 가능한 모든 경우의 일치 여부를 다시 판별한다. 이런 식으로 x가 일치하는 모든 경우에 대해 순서대로 이루어진다는 뜻이다.

법칙 4 위치 문자(assertion)는 다음 표에 따라 일치하여야 한다. 만약 현재 위치에서 위치 문자가 일치하지 않으면, 엔진은 법칙 3으로 되돌아가서 다른 경우에 대해 순서에 맞게 일치하는 지의 여부를 판별한다.

위치 문자
^ 문자열의 시작에서 일치(\m 사용시 행의 시작에서 일치)
$ 문자열의 끝에서 일치(\m 사용시 행의 끝에서 일치)
\b 단어 경계에서 일치(\w와 \W 사이)
\B 단어 경계를 제외한 곳에서 일치
\A 문자열의 시작에서 일치
\Z 문자열의 끝에서 일치
\G 이전의 m//g가 남긴 부분에서 일치
(?=…) … 부분과 미리 일치(패턴에는 포함되지 않음)
(?!…) … 부분과 미리 불일치(패턴에는 포함되지 않음)

$와 \Z위치 문자는 문자열의 끝에서 일치할 뿐 아니라, 문자열의 마지막 문자가 줄바꿈 문자일 경우 한 문자 앞에서도 일치한다.

위치문자 (?=…)와 (?!…)은 글자 폭 0이지만, …부분에 해당하는 정규 표현식의 일치 여부를 미리 판별한다. 기존의 다른 패턴 일치에서와 달리, 위치 문자에 해당하는 패턴이 일치하는지 아닌지만 판별하고 일치된 패턴의 내용 등은 기억하지 않는다.

법칙 5 수량자로 지정된 아톰은 수량자에 의해 허용된 수만큼 일치될 때에 한해 일치된다.(아톰 자체는 법칙 6에 의해 일치된다.) 수량자에 따라서 일치되어야 하는 수도 달라지며, 대부분의 경우 일치되어야 하는 수의 범위를 지정할 수 있게 되어 있다. 여러 번 일치하려면 주어진 문자열 내에서 인접하여 위치해야 한다. 수량자로 특별히 지정되어 있지 않은 아톰은 정확히 한 번 일치하도록 지정된 것으로 간주된다. 수량자는 아래 표에 따라 일치할 횟수를 지정하고 제한한다. 만약 현재 위치에서 수량자로 지정된 임의의 수만큼 일치가 일어나지 않으면, 엔진은 법칙 3으로 되돌아가서 다른 경우에 대해 순서에 맞게 일치하는 지의 여부를 다시 판별하게 된다.

수량자의 종류는 다음과 같다.

최대 최소 허용된 범위
{n, m} {n, m}? 최소한 n번, 그리고 최대 m번 일치
{n, } {n, }? 최소 n번 일치
{n} {n}? 정확하게 n번 일치
* *? 0번 이상 일치({0, }와 같음)
+ +? 1번 이상 일치({1, }와 같음)
? ?? 0번 혹은 1번 일치({0, 1}와 같음)

위의 수량자에서 사용된 {}는 정규 표현식에서만 메타 문자로 취급되며, 그 밖의 다른 문장에서는 일반적인 문자로 취급된다. n과 m은 정수로서 65,536보다 작아야 한다.

{n}의 수량자를 사용할 경우, 수량자 바로 앞의 아톰은 정확히 n번 일치해야 참이 되며, 그렇지 않은 모든 경우는 거짓이 된다. 만약 정확하게 n번 일치하는 경우를 제외하고 일정 범위를 지정한 경우에는, 엔진은 가능한 모든 경우에 대해 일치 여부를 기억해 둔다. 이 때 가장 많이 일치하는 것부터 시도할 지, 가장 적게 일치하는 것부터 시도할 지는 수량자 다음의 ?로 지정한다.

위의 표에서 왼쪽 컬럼에 있는 수량자는 가능한 한 많이 일치할 수 있는 것부터 일치 여부를 판별한다. 이것을 “greedy”(a. 탐욕스러운: 역자 주) 일치라고도 한다. 엔진이 주어진 문자열에서 패턴에 최대 몇 번 일치할 지를 찾기 위해서 일치할 수 있는 가장 큰 숫자부터 시도하지는 않는다. 실제로 엔진은 현재 주어진 문자열에서 수량자 앞의 아톰이 몇 번 일치할 수 있는 지를 알아낸 다음, 가장 큰 숫자부터 하나씩 감소시켜가면서 일치 여부를 판별한다.

예를 들어 /.*foo/와 같은 패턴에서, “foo”에 해당하는 문자열을 찾기 이전에 “.”으로 표시된 “임의의” 글자가 가장 많이 일치할 수 있는 경우(주어진 문자열의 처음부터 끝까지 모두)를 먼저 찾는다. 그리고 나서 “foo”을 일치하려 하나 이미 주어진 문자열은 /.*/에 의해 모두 사용되었기 때문에 “foo”를 일치할 수 없게 된다. 이 때 엔진은 /.*/에 의해 일치될 수 있는 가장 큰 숫자부터 하나씩 줄여나가면서 “foo”를 찾는다. 만일 주어진 문자열에 “foo”가 한 번 이상 나타날 경우, 가장 마지막 “foo”에서 패턴 일치가 종료되며, 그것보다 짧게 일치한 모든 경우는 무시한다.

위와는 반대로, 수량자 바로 다음에 물음표를 추가하여 일치할 수 있는 가장 작은 숫자부터 시도하도록 할 수 있다. 예를 들어 /.*?foo/와 같은 경우, .*?는 가장 먼저 0개의 문자와 일치하는 지를 살펴보고, 그 다음에 1개의 문자, 그리고 나서 2개의 문자와 같은 순서로 “foo”가 일치할 때까지 찾는다. 위의 표에서 최대 일치의 경우와는 반대로, 가장 먼저 “foo”가 나타나면 일치가 종료된다.

법칙 6 각각의 아톰은 아래에 열거한 타입에 의해 일치한다. 만약 해당 아톰이 일치하지 않으면 엔진은 법칙 5로 되돌아가서 다른 숫자에 해당하는 수량자 일치 여부를 판별한다.

아톰은 아래 타입에 의해 일치한다.

괄호로 둘러 싸인 정규 표현식 (…)은, 법칙 2에서 …로 표시되는 정규 표현식과 일치하는 무엇과도 일치한다. 결국 괄호는 수량자를 위한 묶음 연산자(grouping operator)로서 동작한다. 또한 괄호는 일치된 패턴을 순서대로 기억해 두었다가 나중에 재참조(backreference)할 경우에도 사용된다. 이러한 재참조 효과는 (?:…)를 사용하는 경우에는 무시된다.

“.”는 \n을 제외한 어느 문자와도 일치한다. 만일 /s변환자를 사용하는 경우에는 \n과도 일치한다. “.”을 사용하는 가장 큰 목적은 수량자를 사용하여 최소, 혹은 최대한 일치를 판별하기 위한 것이다. .*는 임의의 글자 중 가장 긴 글자와 일치하는 데 비해, .*?는 임의의 글자 중 가장 작은 수의 글자와 일치한다. “.”는 때때로 괄호 안에서 글자의 폭을 나타내는 데에도 사용된다. 예를 들어 /(..):(..):(..)/는 각각 두 글자로 이루어진 3개의 콜론으로 구별된 필드와 일치한다.

[]안에 나열된 문자 리스트는 문자 클래스라고 부르며, 리스트 내의 문자 중 어느 하나와 일치한다. 리스트의 맨 앞에 ^가 오면 위와 반대로 리스트에 없는 글자와 일치한다. 문자의 영역(range)은 a-z와 같은 형태로 표시한다. 아래 표에 나와 있는 것과 같이 문자 클래스 내에서 \d, \w, \s, \n, \r, \t, \f, \nnn등을 사용할 수 있다. 문자 클래스 내에서 \b는 백 스페이스(backspace)를 의미한다. 문자 클래스 내에서 -가 영역을 나타내는 문자로 사용되지 않게 하려면 앞에 역슬래쉬를 붙여 \-와 같이 나타내면 된다. 문자 클래스에서 ]을 사용하려면 앞에 역슬래쉬를 붙이거나 문자 클래스 내의 리스트에서 제일 앞에 오도록 한다. 역시 ^를 사용하기 위해서는 문자 클래스 내의 리스트에서 맨 앞에 오지 않도록 하면 된다. 여기서 주의할 점은, 대부분의 메타 문자가 문자 클래스 내에서는 본래의 의미를 잃어버리게 된다는 것이다. 예를 들어 문자 클래스 내에서 교대 문자열을 지정하는 것은 무의미한데, 문자 클래스 내의 글자는 각 글자별로 하나씩 해석되기 때문이다. 즉, [fee|fie|foe]는 [feio|]와 같은 결과를 갖는다.

역슬래쉬를 붙인 몇몇은 특수 문자 혹은 문자 클래스로 사용된다.

코드 의미
\a 알람(비프음)
\n 개행 문자
\r 캐리지 리턴(carriage return)
\t 탭 문자
\f 폼피드(formfeed)
\e 이스케이프 문자
\d 숫자, [0-9]와 같음
\D 숫자가 아닌 것
\w 단어 문자(알파벳 + 숫자), [a-zA-Z_0-9]와 같음
\W 단어 문자가 아닌것
\s 공백 문자, [\t\n\r\f]와 같음
\S 공백 문자가 아닌것

\w는 단어 문자의 한 글자와 일치하는 것이지 전체 단어와 일치하는 것이 아님을 주목하기 바란다. 단어 전체와 일치하기 위해서는 \w+를 사용하면 된다.

역슬래쉬 뒤에 한 자리 숫자를 붙이면, 앞서 언급했던 재참조(backreference) 문자열 이 된다.(널 문자를 나타내는 은 제외) 만약 재참조 문자열을 가리키는 숫자가 두 자리 이상일 경우(예: \10) 이것은 이 앞에 여러 개의 일치된 문자열이 있다는 것을 의미한다. 이 때 숫자는 0으로 시작하지는 않아야 한다.(8진수와 구분되지 않으므로) 괄호로 지정되는 재참조 문자열은 정규 표현식의 맨 왼쪽 괄호부터 순서대로 번호가 매겨진다.

33과 같이 역슬래쉬 뒤에 2, 3자리의 숫자를 붙일 경우 이것은 해당 값을 갖는 문자와 일치한다.

역슬래쉬 뒤에 x, 그리고 하나 혹은 두 개의 16진수 숫자가 위치하는 경우, 예를 들어 \x7f와 같은 경우 해당하는 값을 갖는 문자와 일치한다.

\cD와 같이 역슬래쉬 다음에 c, 그리고 하나의 문자가 오면 이것은 해당하는 제어 문자와 일치한다.

그 밖의 모든 역슬래쉬 붙은 문자는 해당하는 문자와 일치한다.

기타 언급되지 않은 모든 문자는 해당 문자 자신과 일치한다.

앞서 언급한 대로 \1, \2, \3 등은 패턴의 왼쪽에서 오른쪽으로 순서대로 번호를 매긴, 괄호로 지정된 재참조 문자열을 의미한다. (만약 특정 괄호 뒤에 *등과 같은 수량자가 지정되었을 경우에는 가장 마지막에 일치한 것만이 재참조 문자열이 된다.) 여기서 그러한 재참조 문자열은 주어진 패턴 내의 서브 패턴과 실제로 일치한 것을 의미하는 것이지, 서브 패턴에 해당하는 패턴 일치 룰을 의미하는 것이 아님을 주목해야 한다. 따라서, (0|0x)\d*\s\1\d*는 “0x1234 0x4321″과는 일치하나 “0x1234 01234″와는 일치하지 않는다. 왜냐하면 실제로 서브 패턴 1은 “0x”와 일치한 것이므로, 비록 0|0x에 의해 두 번째 숫자가 0이 올 수 있다 하더라도 \1은 “0”이 될 수는 없다.

정규 표현식의 패턴 밖에서 재참조 문자열을 참조하려면 \대신 $를 사용하여 $1, $2, $3 등과 같이 사용하면 된다. 이들 변수는 자동으로 지역화되고(localized), 변수의 영역(scope) 또한 포함된 블록의 마지막, eval 문자열, 혹은 다음 번 패턴 일치하는 곳 중 가장 먼저 일어나는 곳까지 해당된다.(때때로 \1이 현재 패턴 밖에서 동작하는 경우도 있으므로, \1대신 $1을 사용하는 것이 안전하다.) $+는 맨 마지막 괄호와 일치한 것을 반환한다. $&는 일치된 전체 문자열을 반환하며, $`는 일치된 문자열 앞의 모든 것을 반환한다. $’는 일치된 문자열 다음의 모든 것을 반환한다. 이러한 변수들에 대해 좀 더 자세한 설명이 필요하면, 이 장의 끝부분의 “특수 변수” 절을 참고하기 바란다.

재참조 문자열을 사용하기 위해서 괄호는 개수의 제한 없이 얼마든지 사용할 수 있다. 만약 9개 이상의 괄호로 묶여진 재참조 문자열이 있을 경우, 10번째는 $10, 11번째는 $11로 참조할 수 있다. 앞서 언급한 바와 같이, 패턴 안에서 재참조할 경우에는 \10, \11과 같이 사용한다. 만약 재참조 문자열이 10개나 11개가 안 될경우, \10은 10, \11은 11의 8진수 문자로 취급된다.(물론 \1에서 \9는 항상 재참조 문자열을 지정하는 것으로 사용된다.)

재참조 문자열 사용의 예:

s/^([^ ]+) +([^ ]+)/$2 $1/; # 처음 두 단어를 교환
/(\w+)\s*=\s*\1/; # “foo = foo” 꼴의 일치
/.{80,}/; # 최소 80 문자와 일치
/^(\d+\.?\d*|\.\d+)$/; # 일반적인 숫자와 일치
if (/Time: (..):(..):(..)/) { # 시:분:초 형태에서 각각을 변수로 추출
    $hours = $1;
    $minutes = $2;
    $seconds = $3;
}

힌트: /(…)(..)(…..)/와 같은 패턴을 사용하는 것 대신 unpack 함수를 사용하는 것이 좀 더 효율적이다.

\b는 \w와 \W사이의 한 점으로 정의된다. 여기서 \w와 \W의 순서는 어느 것이 먼저 오더라도 관계없다. 또한 문자열의 첫 글자 앞과 마지막 글자 뒤에 \W이 있다고 가정한다.(문자 클래스 안에서 \b는 단어 경계가 아니라 백 스페이스 문자로 취급됨을 주의하기 바란다.)

Perl에서는 주어진 문자열이 한 줄이라는 가정하에 ^ 문자는 문자열의 시작과, $는 문자열의 끝과 일치한다. 문자열 중간에 포함된 개행문자(newline)는 ^나 $와 일치하지 않는다. 그러나 필요한 경우 주어진 문자열을 여러 줄의 버퍼로 취급하여 매번 나타나는 개행문자의 바로 다음과 ^가 일치하고 개행문자 직전이 $와 일치하도록 할 수 있다. 이렇게 하려면 패턴 일치 연산자에 /m변환자를 사용하면 된다. \A와 \Z는 /m변환자가 사용되더라도 ^나 $처럼 여러 번 일치하지 않는다는 점만 제외하면 ^나 $와 비슷하다. 즉, ^나 $는 주어진 문자열 내부에 포함되어 있는 줄 경계(개행문자로 구분되는)마다 일치되는 데 비해, \A와 \Z는 그렇지 않다는 것이다. 개행문자를 무시하지 않고 주어진 문자열의 실제 끝과 일치하려면 \Z(?!\n)을 사용하면 된다.

여러 줄을 치환할 경우, /s 변환자를 사용하여 주어진 문자열을 한 줄로 취급하도록 하지 않으면 .문자는 개행문자와 일치하지 않는다.(/s 변환자는 $*변수의 설정값을 무시한다.) 아래 예제에서 $_에는 개행문자가 남게 된다.

$_ = <STDIN>;
s/.*(some_string).*/$1/;

개행문자를 제거하고 싶으면 다음 중 어느 한 가지 방법을 사용하면 된다.

s/.*(some_string).*/$1/s;
s/.*(some_string).*\n/$1/;
s/.*(some_string)[^]*/$1/;
s/.*(some_string)(.|\n)*/$1/;
chop; s/.*(some_string).*/$1/;
/(some_string)/ && ($_ = $1);

Perl에서 모든 메타 문자는 \b, \w, \n 등과 같이 역슬래쉬와 알파벳 문자로 이루어져 있다. 다른 몇몇 정규 표현식 언어와 달리 \\, \(, \), \<, \>, \{, \}등과 같이 알파벳 문자가 아닌 것으로 메타 문자를 사용하지는 않기 대문에, 특정 문자열을 패턴으로 사용하고자 할 때 그 안에 특정 메타 문자가 포함되었는지를 염려할 필요가 없다. 알파벳 문자가 아닌 문자 모두를 인용하기 위해서는 다음과 같이 하면 된다.

$pattern =~ s/(\W)/\\$1/g;

또는 quotemeta 함수를 사용해서 위와 같이 할 수 있다. 위의 예제에서 일치 연산자 =~ 우변의 내용을 다음과 같이 쉽게 표현할 수도 있다.

/$unquoted\Q$quoted\E$unquoted/

정규 표현식에서 교대 문자열을 괄호로 묶어 주지 않을 경우, 다음과 같은 현상을 겪을 수 있다. 예를 들어

/^fee|fie|foe$/

는 문자열의 처음에 “fee”가 오거나, 문자열의 임의의 장소에 “fie”가 있거나, 문자열의 마지막에 “foe”가 오면 일치한다. 그러나

/^(fee|fie|foe)$/

에서는 문자열이 “fee”거나 “fie”, 혹은 “foe”이어야만 일치한다.

정규 표현식의 확장

Perl에서는 정규 표현식의 확장을 위한 몇 가지 방법을 제공한다. 이 중 몇 가지는 앞 부분에서 이미 살펴보았던 것이다. 표현식의 확장은 괄호 안에 물음표로 시작하는 한 쌍의 괄호로 만들어진다. 물음표 바로 다음의 문자가 정규 표현식 확장의 기능을 지정한다.

(?#text)

주석문이며 텍스트의 내용은 무시된다. 공백 문자를 사용 가능하게 하기 위해 /x를 사용할 경우 단지 #만 남게 된다.

(?:…)

“(…)”와 같은 기능을 하나 재참조 문자열을 만들지는 않는다. 따라서

split(/\b(?:a|b|c)\b/)

split(/\b(a|b|c)\b/)

와 같지만, 실제로 $1에는 아무 것도 저장하지 않는다. 따라서 두 번째 split 문장에서와는 달리 첫 번째 split은 여분의 구분자를 내어 놓지는 않는다.

(?=…)

0폭을 갖는 긍정 위치문자(positive lookahead assertion)이다. 예를 들어 /\w+(?=\t)/는 어느 단어 다음에 탭 문자가 오는 것과 일치하지만 $&에 탭 문자를 저장하지는 않는다.

(?!…)

0 폭을 갖는 부정 위치문자(negative lookahead assertion)이다. 예를 들어 /foo(?!bar)/는 “bar”가 바로 뒤에 오지 않는 임의의 “foo”와 일치한다. 여기서 주의할 점은, lookahead와 lookbehind는 다르다는 점이다. 따라서 /(?!foo)bar/와 같이 하여 “bar”앞에 “foo”가 아닌 것이 오는 것과 일치하도록 할 수는 없다는 것이다. 왜냐하면, (?!foo)는 단지 다음에 올 내용이 “foo”가 아니라는 뜻이기 때문이다. 따라서 “foobar”와 일치하게 된다. 이 경우 /(?!foo)…bar/와 같이 하면 할 수 있겠지만, “bar”앞에 세 글자가 아닌 다른 문자열이 오는 것을 처리할 수는 없게 된다. 따라서 /(?:(?!foo)…|^..?)bar/와 같이 하거나 좀 더 쉽게

if (/foo/ and $` !~ /bar$/)

와 같이 하면 된다.

(?imsx)

하나 혹은 그 이상의 패턴 일치 변환자가 포함되어 있음을 뜻한다. 이것은 특정 테이블 내의 패턴 중 일부는 대소문자 구별이 되어야 하고, 일부는 그렇지 않은 경우와 같은 때 유용하다. 대소문자 구별이 필요한 경우는 패턴의 앞부분에 (?i)를 붙이면 된다. 예를 들면,

# hardwired case insensitivity
$pattern = “buffalo”;
if ( /$pattern/i )
# data-driven case insensitivity
$pattern = “(?i)buffalo”;
if ( /$pattern/ )

패턴 일치 연산자

m/PATTERN/gimosx
/PATTERN/gimosx

이 연산자는 주어진 문자열에서 패턴을 검색하고, 그 결과로 스칼라 구문에서 참(1)이나 거짓(“”)을 반환한다. =~나 !~연산자에서 문자열을 지정하지 않았을 경우에는 기본 문자열 변수 $_을 검색한다.(=~로 지정된 문자열은 lvalue일 필요는 없으며, 특정 구문의 평가 결과값이 올 수도 있다. =~는 결합의 우선 순위가 높으므로 구문의 평가 결과를 사용할 경우에는 해당 구문을 괄호로 묶어 주어야 한다.)

변환자는 다음과 같다

변환자
g 전역 일치(global match)
i 대소문자 구분하지 않고 일치
m 문자열을 여러 줄로 취급
o 패턴을 한번만 컴파일
s 문자열을 한줄로 취급
x 확장 정규 표현식 사용

/를 구분자로 사용할 경우 패턴 앞에 위치하는 m은 지정하지 않아도 된다. m을 사용하는 경우, 영문자나 공백 문자가 아닌 임의의 문자를 구분자로 사용할 수 있다. 예를 들어 파일명에 “/”이 포함된 경우 다른 문자를 구분자로 사용할 수 있으므로 매우 유용하다.

PATTERN은 변수를 포함할 수 있으며 매번 패턴이 검색될 때마다 패턴 내의 변수는 치환된다.(즉, 패턴이 재컴파일되는 것이다. 여기서 $)와 $|는 치환되지 않음을 주목하기 바란다.) 만약 패턴을 처음 한 번만 컴파일하기 위해서는 마지막 구분자 다음에 /o를 추가하면 된다. 이 변환자를 사용하면 매번 패턴 검색시 패턴의 재컴파일을 하지 않게 되므로 프로그램의 수행 속도에 도움을 준다. 이 경우 패턴 내에 포함되어 있는 변수의 값이 매 검색시 변화하지 않아야 한다. 만약 변수의 값이 계속 변화하더라도 /o를 지정하면 Perl은 패턴을 처음 한 번만 컴파일하기 때문에 변화된 변수의 값이 패턴에 반영되지 않는다.

널 문자열에 PATTERN을 적용할 경우, 가장 최근에 성공적으로 수행된 안쪽 블록의 정규 표현식(split, grep, map 포함)이 대신 사용된다.

스칼라가 아니라 리스트 값을 필요로 하는 구문에 사용될 경우, 일치된 결과는 패턴 내에서 괄호로 지정된 재참조 문자열의 리스트를 반환한다. 즉 ($1, $2, $3 …)을 반환하게 되며, $1등과 같은 변수의 값도 마찬가지로 지정된다. 만약 패턴 일치가 실패하면, 널 리스트가 반환된다. 패턴이 일치했더라도 패턴 내에 괄호로 지정된 재참조 문자열이 없으면 리스트 값 (1)이 반환된다. 예는 다음과 같다.

# 대소문자 구별없이 일치
open(TTY, ‘/dev/tty’);
<TTY> =~ /^y/i and foo(); # do foo() if they want it

# 서브 문자열을 추출
if (/Version: *([0-9.]+)/) { $version = $1; }

# 구분자로 / 외의 문자를 사용
next if m#^/usr/spool/uucp#;

# grep 의 가장 간단한 기능
$arg = shift;
while (<>) {
    print if /$arg/o; # 한 번만 컴파일
}

# 처음 두 단어, 그리고 그 나머지를 리스트로 반환
if (($F1, $F2, $Etc) = ($foo =~ /^\s*(\S+)\s+(\S+)\s*(.*)/))

마지막 예는 $foo를 처음 두 단어와 그 나머지로 분리한 다음 각각 $F1, $F2, $Etc에 대입하게 된다. if 문장의 조건식은 이들 변수 중 어느 하나의 값이라도 대입이 되면 참이 된다. 즉, 패턴이 일치하면 참이 되는 것이다. 실제로는 다음과 같이 split을 사용하여 간단하게 표시한다.

if (($F1, $F2, $Etc) = split(‘ ‘, $foo, 3))

/g 변환자는 전역 패턴 일치(global pattern matching)를 지정한다. 즉, 주어진 문자열 내에서 가능한 한 여러 번 패턴 일치를 찾게 한다. /g 변환자가 실제로 어떻게 동작하는 지는 사용되는 구문에 따라 달라진다. 리스트 구문에서는, 정규 표현식 안에 괄호로 지정된 재참조 문자열에 의해 일치된 모든 부 문자열(substring)로 이루어진 리스트를 반환한다. 만약 괄호가 하나도 없는 경우는 마치 전체 패턴을 괄호로 둘러싼 것과 같이 일치된 문자열 모두로 이루어진 리스트를 반환한다.

스칼라 구문에서 m//g를 사용하면 매번 주어진 문자열과 일치할 때마다 참을 반환하고, 일치가 모두 끝나면 거짓을 반환한다.(다시 말해서 바로 전에 일치가 일어난 곳을 기억했다가 그 지점부터 다시 일치 여부를 판별한다는 뜻이다. 문자열 내에서 현재 일치 여부를 판별하는 지점은 pos 함수를 사용하여 알아낼 수 있다. 제 3 장을 참조) 예를 들면,

# LIST context–extract three numeric fields from uptime command
($one,$five,$fifteen) = (`uptime` =~ /(\d+\.\d+)/g);
# scalar context–count sentences in a document by recognizing
# sentences ending in [.!?], perhaps with quotes or parens on
# either side. Observe how dot in the character class is a literal
# dot, not merely any character.
$/ = “”; # paragraph mode
while ($paragraph = <>) {
    while ($paragraph =~ /[a-z][‘”)]*[.!?]+[‘”)]*\s/g) {
        $sentences++;
    }
}
print “$sentences\n”;
# find duplicate words in paragraphs, possibly spanning line boundaries.
# Use /x for space and comments, /i to match the both `is’
# in “Is is this ok?”, and use /g to find all dups.
$/ = “”; # paragrep mode again
while (<>) {
    while ( m{
                \b # start at a word boundary
                (\w\S+) # find a wordish chunk
                (
                    \s+ # separated by some whitespace
                    \1 # and that chunk again
                ) + # repeat ad lib
                \b # until another word boundary
            }xig
        )
    {
        print “dup word `$1′ at paragraph $.\n”;
    }
}

?PATTERN?

이것은 reset 연산자 사이에서 단 한번만 일치한다는 점을 제외하면 /PATTERN/과 같은 기능을 한다. 예를 들어 여러 개의 파일을 검색하여 각 파일 내에 특정한 패턴이 처음 나오는 것까지만 알고 싶을 경우에 유용하게 사용된다.

s/PATTERN/REPLACEMENT/egimosx

이것은 주어진 문자열에서 PATTERN을 찾으면, 일치된 것을 REPLACEMENT와 바꾸고 실제로 교환된 것의 개수를 반환한다. /g 변환자를 사용할 경우에는 교환된 개수가 1보다 클 수 있다. 일치되지 않은 경우에는 거짓(0)을 반환한다.

=~나 !~에서 문자열을 지정하지 않았을 경우에는 기본 문자열 변수 $_을 검색하고 변환한다.(=~에 의해 지정된 문자열은 스칼라 변수, 배열의 요소, 해쉬 요소, 혹은 이들 중 하나의 대입이어야 한다. 즉, lvalue이어야 한다.)

만약 구분자로 홑 따옴표를 사용하면, PATTERN이나 REPLACEMENT에 해당하는 부분에서 변수 치환은 일어나지 않는다. 홑 따옴표 이외의 문자를 구분자로 사용하는 경우에는 $를 문자열의 마지막 여부를 판단하는 문자 대신 변수로 생각하여 패턴 검색시마다 변수 치환이 이루어진다. 처음 한 번만 변수 치환을 하고 그 다음부터는 변수의 값을 치환하고 싶지 않을 경우에는 /o 변환자를 사용하면 된다. PATTERN이 널 문자열에 적용되면, 가장 최근에 성공적으로 사용된 정규 표현식이 대신 사용된다. REPLACEMENT 역시 PATTERN 과 마찬가지로 변수 치환이 이루어진다. PATTERN이 패턴 일치 연산자가 평가될 때마다 변수 치환이 이루어지는데 비해, REPLACEMENT PATTERN이 일치할 때마다 치환이 이루어진다.(/g 변환자를 사용하면 한 번의 패턴 일치 문장에서 PATTERN을 여러 번 일치하도록 할 수 있다.)

변환자는 다음과 같은 것이 있다.

변환자
e 오른쪽의 내용을 표현식으로 평가
g 전역 일치 및 교환(global replace)
i 대소문자 구분하지 않고 일치
m 문자열을 여러줄로 취급
o 패턴을 한번만 컴파일
s 문자열을 한줄로 취급
x 확장 정규 표현식 사용

임의의 비 알파벳 문자, 비 공백 문자를 /대신 구분자로 사용할 수 있다. 구분자로 홑 따옴표가 사용되는 경우 교환되는 문자열을 별도로 해석하지는 않는다.(/e 변환자를 사용하여 이것을 무시할 수 있다.) 만약 PATTERN을 괄호 등과 같이 한 쌍으로 이루어진 구분자로 묶을 경우, REPLACEMENTPATTERN에서 사용한 것과 다른 구분자를 사용할 수 있다. 예를 들어 s(foo)(bar)혹은 s<foo>/bar/와 같이 표기할 수 있다. /e 변환자를 사용하면 교환되는 문자열 부분을 변수 치환 정도만 일어나는 겹 따옴표 대신, 마치 Perl 문장으로 해석하게끔 할 수 있다.(이것은 eval의 경우와 비슷하다. 그러나 문법이 컴파일시에 체크된다는 점이 다르다.)

# don’t change wintergreen
s/\bgreen\b/mauve/g;
# avoid LTS with different quote characters
$path =~ s(/usr/bin)(/usr/local/bin);
# interpolated pattern and replacement
s/Login: $foo/Login: $bar/;
# modifying a string “en passant”
($foo = $bar) =~ s/this/that/;
# counting the changes
$count = ($paragraph =~ s/Mister\b/Mr./g);
# using an expression for the replacement
$_ = ‘abc123xyz’;
s/\d+/$&*2/e; # yields ‘abc246xyz’
s/\d+/sprintf(“%5d”,$&)/e; # yields ‘abc 246xyz’
s/\w/$& x 2/eg; # yields ‘aabbcc 224466xxyyzz’
# how to default things with /e
s/%(.)/$percent{$1}/g; # change percent escapes; no /e
s/%(.)/$percent{$1} || $&/ge; # expr now, so /e
s/^=(\w+)/&pod($1)/ge; # use function call
# /e’s can even nest; this will expand simple embedded variables in $_
s/(\$\w+)/$1/eeg;
# delete C comments
$program =~ s {
    /\* # Match the opening delimiter.
    .*? # Match a minimal number of characters.
    \*/ # Match the closing delimiter.
} []gsx;
# trim white space
s/^\s*(.*?)\s*$/$1/;
# reverse 1st two fields
s/([^ ]*) *([^ ]*)/$2 $1/;
tr/SEARCHLIST/REPLACEMENTLIST/cds
y/SEARCHLIST/REPLACEMENTLIST/cds

엄격하게 말해서 이들 연산자는 정규 표현식을 사용하지 않기 때문에 패턴 일치 항목에 속하지는 않는 것이다. 이들은 주어진 문자열을 문자별로 하나씩 살펴보면서, SEARCHLIST에 속하는 문자가 발견되면 해당 문자를 REPLACEMENTLIST에 해당 문자와 바꾸는 일을 한다. 그리고 실제로 교환되거나 지워진 글자의 숫자를 반환한다. =~나 !~로 지정된 문자열이 없을 경우 문자열 $_이 대신 사용된다.(=~에 의해 지정된 문자열은 스칼라 변수, 배열의 요소, 해쉬 요소, 혹은 이들 중 하나의 대입이어야 한다. 즉, lvalue이어야 한다.) y는 tr///와 같은 표현이다. 만약 SEARCHLIST를 괄호 등과 같이 한 쌍으로 이루어진 구분자로 둘러 쌓게 될 경우, REPLACEMENTLISTPATTERN에서 사용한 것과 다른 구분자를 사용할 수 있다. 예를 들어 tr[A-Z][a-z]이나 tr(+-*/)/ABCD/등과 같이 나타낼 수 있다.

변환자로는 다음과 같은 것이 있다.

변환자
c Searchlist의 반대
d 찾았으나 교환되지 않은 문자를 삭제
s 중복되는 글자를 압축

/c 변환자를 사용하면 SEARCHLIST 문자 세트가 반전된다. 즉, 실제로는 SEARCHLIST에 포함되지 않은 문자에 대해 검색이 이루어진다. /d 변환자를 사용하면, SEARCHLIST에 지정되어있으나 REPLACEMENTLIST에 주어지지 않은 글자는 삭제된다. 만약 /s 변환자가 사용된 경우에는, 같은 글자로 변환되는 일련의 문자는 한 글자로 압축된다.

/d 변환자가 사용되면, REPLACEMENTLIST 는 지정된 대로 정확하게 해석된다. /d 변환자를 사용하지 않는 경우에는, 만일 REPLACEMENTLISTSEARCHLIST 보다 짧으면 REPLACEMENTLIST 의 마지막 글자가 필요한 숫자만큼 반복된다. REPLACEMENTLIST 가 널이면 SEARCHLIST 가 복사되어 사용된다. 이것은 클래스 내에서 글자의 개수를 헤아릴 때나, 글자를 압축할 때 유용하다. 예는 다음과 같다.

$ARGV[1] =~ tr/A-Z/a-z/; # 대문자를 소문자로 변환
$cnt = tr/*/*/; # $_에 포함된 ‘*’의 개수
$cnt = $sky =~ tr/*/*/; # $sky에 포함된 ‘*’의 개수
$cnt = tr/0-9//; # $_에 포함된 숫자의 개수
tr/a-zA-Z//s; # bookkeeper를 bokeper로 압축
($HOST = $host) =~ tr/a-z/A-Z/;
tr/a-zA-Z/ /cs; # 비 영문자를 공백으로 변환
tr [\(rs200-\(rs377]
[\(rs000-\(rs177]; # 8번째 비트를 삭제함

한 글자에 대해 여러 가지로 변환될 수 있을 경우에는 가장 처음 것만이 사용된다.

tr/AAA/XYZ/

에서는 모든 A는 X로 변환된다.

이러한 문자 변환 테이블은 컴파일시 만들어지므로, SEARCHLISTREPLACEMENTLIST 안에서 변수 치환을 사용하려면 반드시 eval을 사용해야 한다. 예를 들면,

eval “tr/$oldlist/$newlist/”;
die $@ if $@;
eval “tr/$oldlist/$newlist/, 1” or die $@;

만약 주어진 문자열을 모두 대문자나 소문자로 바꾸고 싶을 경우, tr/a-z/A-Z대신 가급적 해당 시스템의 로케일 정보를 이용할 수 있는 \U나 \L을 사용하는 것이 좋다.

연산자

연산자를 사용하여 표현식의 용어를 묶거나 변경시킬 수 있다. 각 연산자는 우선 순위가 있어서 근접한 용어와 결합하는 순서가 정해진다. Perl에서는 다음 테이블에서와 같이 높은 쪽에서 낮은 쪽으로 연산자 결합의 우선 순위가 정해져 있다.

결합성 연산자
왼쪽 일반 용어와 리스트 연산자(왼쪽)
왼쪽 ->
비결합 ++ —
오른쪽 **
오른쪽 ! ~ \ 일원 연산자 +와 –
왼쪽 =~ !~
왼쪽 * / % x
왼쪽 + – ,
왼쪽 << >>
비결합 이름붙은 일원 연산자
비결합 < > <= >= lt gt le ge
비결합 == != <=> eq ne cmp
왼쪽 &
왼쪽 | ^
왼쪽 &&
왼쪽 ||
비결합 .
오른쪽 ?:
오른쪽 = += -= *= 등
왼쪽 , =>
비결합 리스트 연산자(오른쪽)
오른쪽 not
왼쪽 and
왼쪽 or xor

실제로 연산자의 우선 순위가 너무 많다고 생각할 수도 있을 것이다. 그러나 대부분의 우선 순위는 직관적으로 생각되는 순서대로 정해져 있으므로 크게 혼동되지는 않을 것이며, 필요에 따라 괄호를 사용하면 얼마든지 우선 순위를 명확하게 지정할 수 있다는 것을 명심해 두기 바란다.

C 언어에서 빌려온 대부분의 연산자는 C에서와 같은 우선 순위를 갖고 있다.

다음 절부터는 위의 연산자들에 대해 우선 순위가 높은 것부터 차례로 살펴보도록 하겠다. 극히 드문 경우를 제외하고 대부분의 연산자는 스칼라 값을 취한다. 그렇지 않은 경우는 해당 연산자를 설명할 때 언급하도록 하겠다.

용어와 리스트 연산자 (왼쪽)

Perl에서 용어는 가장 높은 우선 순위를 갖는다. 이러한 용어로는 변수, 인용문, 인용문 형태의 연산자, 괄호로 둘러 싸인 표현식, 피연산자를 괄호로 둘러 싼 함수 등이 해당된다.

여기에는 아주 간단하면서도 중요한 몇 가지 법칙이 있다. 만약 print와 같은 리스트 연산자나 chdir과 같은 이름붙은 일원 연산자 뒤에 다음번 토큰으로 왼쪽 괄호가 오면, 해당 연산자와 괄호로 둘러 싸인 피연산자는 일반적인 함수 호출과 마찬가지로 가장 높은 우선 순위를 갖는다. 여기서의 법칙은 다음과 같다: 만약 함수 호출과 비슷한 형태이면, 함수 호출로 생각하면 된다. 물론 필요에 따라 피연산자 앞에 일원 + 연산자를 사용하여 실제로 아무 일도 하지 않으면서 함수 호출이 아닌 것처럼 만들 수도 있다.

예를 들어, ||는 chdir보다 우선 순위가 낮으므로 다음과 같이 된다.

chdir $foo || die; # (chdir $foo) || die
chdir($foo) || die; # (chdir $foo) || die
chdir ($foo) || die; # (chdir $foo) || die
chdir +($foo) || die; # (chdir $foo) || die

그러나 *는 chdir보다 우선 순위가 높으므로 다음과 같은 결과를 얻는다.

chdir $foo * 20; # chdir ($foo * 20)
chdir($foo) * 20; # (chdir $foo) * 20
chdir ($foo) * 20; # (chdir $foo) * 20
chdir +($foo) * 20; # chdir ($foo * 20)

수량 연산자도 비슷한 결과를 만든다.

rand 10 * 20; # rand (10 * 20)
rand(10) * 20; # (rand 10) * 20
rand (10) * 20; # (rand 10) * 20
rand +(10) * 20; # rand (10 * 20)

괄호가 없으면 printsort, chmod등과 같은 리스트 연산자의 우선 순위는 아주 높거나 혹은 아주 낮으며, 이것은 연산자의 왼쪽에 위치하는 지 오른쪽에 위치하는 지에 따라 결정된다.(이 절의 제목에 (왼쪽)이라는 단어가 붙은 것도 바로 이 때문이다.) 예를 들어

@ary = (1, 3, sort 4, 2);
print @ary; # prints 1324

의 경우, sort의 오른쪽에 위치한 쉼표는 sort보다 먼저 평가되지만, sort 왼쪽의 쉼표는 sort보다 나중에 평가된다. 다시 말해 리스트 연산자는 해당 연산자 이후의 모든 피연산자를 대상으로 평가하려 한다는 것이다. 아래 예제를 괄호에 주의하며 살펴보기 바란다.

# These evaluate exit before doing the print:
print($foo, exit); # Obviously not what you want.
print $foo, exit; # Nor is this.
# These do the print before evaluating exit:
(print $foo), exit; # This is what you want.
print($foo), exit; # Or this.
print ($foo), exit; # Or even this.

또한 다음의 경우,

print ($foo & 255) + 1, “\n”; # prints ($foo & 255)

아마 대부분 예상했던 결과가 출력되지 않았을 것이다. 다행스럽게도 위와 같은 Perl프로그램 실행시 -w을 사용하면 “Useless use of addition in a void context” 경고 메시지를 출력하기 때문에 미리 쉽게 발견할 수 있다.

이러한 용어로 구분되는 다른 것들로는 do {}나 eval {}구조, 서브루틴 호출, 메쏘드 호출, 익명 배열과 해쉬 컴포저 [], {}, 익명 서브루틴 컴포저 sub {}등이 있다.

화살표 연산자

C나 C++에서와 마찬가지로, ->는 객체를 참조하는데 사용되는 삽입(infix) 연산자이다. 화살표 연산자의 우측에 […]나 {…}꼴의 첨자가 오게 되면, 연산자의 왼쪽에는 배열이나 해쉬를 가리키는 레퍼런스가 와야 한다.(자세한 내용은 제 4장 참조)

화살표 연산자의 우측에 […]나 {…}꼴의 첨자가 아닌 것이 오게 되는 경우에는, 우측에는 반드시 메쏘드 이름이나 메쏘드 이름을 갖고 있는 단순한 스칼라 변수가 와야 하며, 연산자의 좌측에는 객체나 클래스 이름(즉, 패키지 이름)이 와야 한다.(제 5장 참조)

자동 증가 및 자동 감소 연산자

++과 –연산자는 C에서와 같은 방식으로 동작한다. 즉, 변수 앞에 놓이게 되면 해당되는 값을 반환하기 전에 변수를 미리 증가시키거나 감소시키고, 변수 뒤에 놓이게 되면 해당하는 값을 반환한 후에 변수를 증가시키거나 감소시킨다. 예를 들어 $a++는 스칼라 변수 $a의 값을 먼저 반환한 다음 증가시킨다. 마찬가지로 –$b{(/(\w+)/)[0]}은 기본 검색 변수 $_에서 첫 번째 단어로 인덱스되는 해쉬 %b의 요소 값을 감소시킨 후에 그 값을 반환한다.

자동 증가 연산자는 다음과 같은 부수적인 기능도 갖고 있다. 숫자 값이나 숫자 구문에서는 우리가 일반적으로 알고 있는 대로 동작하지만, 문자열 구문에서 동작할 때에는 조금 다르게 동작한다. 피연산자가 문자열이면서 /^[a-zA-Z]*[0-9]*$/ 패턴과 일치하는 경우 문자열에 대해서도 증가가 이루어지며, 각 글자는 해당 글자의 영역 내의 값을 유지하면서 자리 올림이 이루어진다. 예는 다음과 같다.

print ++($foo = ’99’); # prints ‘100’
print ++($foo = ‘a0’); # prints ‘a1’
print ++($foo = ‘Az’); # prints ‘Ba’
print ++($foo = ‘zz’); # prints ‘aaa’

그러나 자동 감소 연산자는 이 같은 기능을 갖고 있지 않다.

누승 연산자

**는 누승 연산자이다. 누승 연산자는 일진 연산자 보다 우선 순위가 높다는 것을 주의해야 한다. 따라서 2**4는 (2**4)이지 (-2)**4가 아니다. 누승 연산자는 C의 pow(3)함수를 사용해 구현되었다. 누승 연산자는 로그를 이용해서 계산하므로, 정수 외에 분수값에 대해서도 동작하지만, 때때로 실제로 n번 곱해서 얻는 결과에 비해 부정확한 값을 내기도 한다.

일원 연산자

일원 연산자 !는 논리적 부정 연산자로서 “not”에 해당한다. 그러나 연산자 not은 !보다 우선 순위가 낮다. 피연산자가 거짓(숫자 0, 문자열 “0”, 널 문자열, 혹은 정의되지 않은 값)인 경우 !연산자를 적용한 값은 1이고, 그렇지 않으면 널 문자열이 된다.

일원 연산자는 산술적 부정 연산자로서, 피연산자가 숫자인 경우 음수는 양수, 양수는 음수로 변환한다. 피연산자가 식별자인 경우는, -와 식별자를 연결한 문자열을 반환한다. 그 밖에 문자열이 +나 -로 시작하는 경우는 일원 연산자 -를 적용하면 반대되는 부호로 시작하는 문자열이 반환된다. 따라서 bareword는 “-bareword”와 같은 효과를 갖는다. 이러한 특성은 Tk나 CGI 프로그래머에게 유용하게 쓰인다.

일원 연산자 ~는 비트 단위로 부정 연산을 수행한다. 즉, 1의 보수를 만든다. 예를 들어 32비트 컴퓨터에서 ~123은 4292967172이다.

일원 연산자 +는 별다른 특별한 기능은 없다. 문법적으로는 함수의 이름과 함수의 피연산자의 리스트로 해석될 수 있는 괄호로 묶인 표현식을 구분하는데에 유용하게 쓰인다.(“용어와 리스트 연산자” 절의 예제를 참고)

일원 연산자 \는 피연산자의 레퍼런스를 만들어낸다. 일원 연산자 \와 문자열 내에서 역슬래쉬가 바로 다음에 오는 것에 대한 특별한 해석을 하지 못하게끔 한다는 것이 같을지라도 각각의 기능을 혼동하지 않도록 주의해야 한다.

\ 연산자는 리스트에서 괄호로 둘러 싸인 리스트 값에 사용되기도 한다. 이 경우 리스트의 각 요소에 해당하는 레퍼런스를 반환한다.

바인딩 연산자

이진 연산자 =~는 스칼라 표현식과 결합하여 패턴의 일치나 문자열의 치환, 변환을 할 때 사용된다. 주어진 문자열이 없을 경우에는 기본적으로 문자열 변수 $_에 대해 적용된다. 바인딩 연산자의 우변에는 검색 패턴이나, 치환, 변환 리스트가 오고, 왼쪽에는 기본 문자열 변수 $_대신 검색하거나 치환, 변환하고자 하는 문자열이 온다. 연산의 반환값으로는 해당 연산이 성공적으로 수행되었는지의 결과가 나온다. 바인딩 연산자의 우변에 검색 패턴, 치환, 변환 리스트 대신 표현식이 오게 되면, 실제로 수행시에 검색 패턴으로 해석된다. 즉, $_ =~ $pat은 $_ =~ /$pat/과 동일하게 취급된다. 이같은 경우는 명확히 검색 패턴을 지정하는 경우에 비해 매 검색시마다 해당 표현식을 재컴파일해야 하기 때문에 비효율적이다.(만약 /$pat/o와 같이 /o 변환자로 지정한 경우에는 처음 한 번을 제외하고는 재컴파일하지 않는다.)

이진 연산자 !~는 반환값이 논리적으로 반대가 된다는 것을 제외하고는 =~와 같다. 따라서 다음 두 가지 표현식은 기능적으로 동등하다.

$string !~ /pattern/
not $string =~ /pattern/

위에서 언급한 대로 반환값으로는 해당 연산의 성공 여부가 반환된다. 치환이나 변환의 경우 성공적으로 치환, 변환된 개수가 반환된다.(실제로 변환 연산은 글자의 개수를 세는 데 자주 사용된다.) 0이 아닌 모든 값은 참이므로 위에서 말한 반환값은 일반적인 조건식에서 정상적으로 동작한다. 반환되는 참 값 중에서 가장 특별한 형태는 리스트 값이다. 리스트에서는 패턴 내에 괄호로 지정된 재참조 문자열에 해당하는 서브 문자열을 반환한다. 따라서 일치된 결과가 리스트에 대입되면 그 결과는 참이 된다. 아래 예에서 하나씩 살펴보자.

if ( ($k,$v) = $string =~ m/(\w+)=(\w*)/ ) {
    print “KEY $k VALUE $v\n”;
}

우선 =~연산자는 $string과 결합하여 연산자 우변의 패턴에 대한 일치를 수행한다. 이 패턴은 주어진 문자열 $string내에서 KEY=VALUE 형태의 모든 패턴을 검색한다. 이 문장은 리스트 대입의 우변에 해당하므로 리스트 구문이 된다. 만약 패턴 일치가 일어나면, $k와 $v에 리스트 대입이 수행된다. 이 리스트 대입 자체는 스칼라 구문 내에 들어 있으므로(if 의 조건식이므로) 성공적으로 일치된 개수 2가 반환된다. 2는 참이므로(0 이 아닌 모든 값은 참) 따라서 if의 조건식은 참이 된다. 만약 패턴 일치가 일어나지 않으면, 어떤 값도 대입되지 않게 되며 따라서 0을 반환하여 조건식은 거짓이 된다.

곱셈 연산자

Perl은 C언어와 비슷하게 *, /, % 연산자를 제공한다. *와 /은 다른 언어와 마찬가지로 두 수를 곱하고 나눈다. 정수 라이브러리 모듈을 사용하지 않는 한 나눗셈은 실수 연산으로 행해진다.

% 연산자는 나머지를 구하기 위한 연산을 수행하기 이전에 피연산자를 먼저 정수로 변환한다. 따라서 만약 실수에서 나머지 연산을 수행하려 하는 경우에는 POSIX 모듈의 fmod()함수를 사용하면 된다.

이진 연산자 x는 반복 연산자이다. 스칼라 구문에서는 연산자의 좌변에 지정된 문자열을 우변에 지정된 횟수만큼 반복하여 연결한 결과를 반환한다.

print ‘-‘ x 80; # ‘-‘를 80개 출력

print “\t” x ($tab/8), ‘ ‘ x ($tab%8); # 탭 제거

리스트 구문에서는 왼쪽 피연산자가 괄호에 둘러 싸인 리스트인 경우, 리스트 반복 연산자로 동작한다. 이것은 배열의 모든 값을 특정한 값으로 초기화하는 데 유용하게 사용된다.

@ones = (1) x 80; # 80개의 1을 갖는 리스트
@ones = (5) x @ones; # 모든 리스트 요소를 5로 설정

비슷한 방법으로, x 연산자를 사용하여 배열이나 해쉬의 일부분을 초기화할 수도 있다.

@keys = qw(perls before swine);
@hash{@keys} = (“”) x @keys;

위의 예제가 잘 이해되지 않는다면 다음 사항을 염두에 두고 이해하면 된다. 즉, 두 번째 문장에서 @keys는 대입 연산의 좌변에서는 리스트로 사용되었으며, 우변에서는 스칼라(배열의 길이)로 사용되었다. 따라서 위의 예제는 다음과 같은 효과를 갖는다.

$hash{perls} = “”;
$hash{before} = “”;
$hash{swine} = “”;

덧셈 연산자

Perl에서도 +와 – 연산자를 제공하고 있다. 이들 연산자는 모두 필요한 경우 피연산자를 문자열에서 숫자로 변환하고, 결과값으로는 숫자를 반환한다.

이 외에도 Perl에서는 문자열 연결 연산자 “.”를 제공한다. 예를 들면

$almost = “Fred” . “Flintstone”; # FredFlintstone을 반환

Perl에서 문자열 연결이 이루어질 때 연결되는 문자열 사이에 공백이 포함되지 않음을 주의해야 한다. 만일 문자열 연결시 스페이스를 포함하고 싶거나, 두 개 이상의 문자열을 연결하려 하는 경우에는 3장에서 언급될 join 연산자를 사용하면 된다. 대개는 다음과 같이 겹 따옴표 안에서 묵시적으로 문자열 연결을 수행한다.

$fullname = “$firstname $lastname”;

쉬프트 연산자

비트 쉬프트 연산자(<<와 >>)는 좌변의 인수를 우변의 지정된 수 만큼 왼쪽(<<)이나 오른쪽(>>)으로 쉬프트 한 결과를 반환한다. 이 때 인수는 정수이어야 한다. 예를 들면

1 << 4; # 16을 반환
32 >> 4; # 2를 반환

이름 붙은 일원 연산자 및 파일 테스트 연산자

3장에 소개될 몇몇 함수는 실제로는 일원 연산자이며, Perl의 일원 연산자는 다음과 같다.

-X(file tests)

gethostbyname

localtime

rmdir

alarm

getnetbyname

log

scalar

caller

getpgrp

lstat

sin

chdir

getprotobyname

my

sleep

chroot

glob

oct

sqrt

cos

gmtime

ord

srand

defined

goto

quotemeta

stat

delete

hex

rand

uc

do

int

readlink

ucfirst

eval

lc

ref

umask

exists

lcfirst

require

undef

exit

length

reset

 

exp

local

return

 

 

이들은 모두 일원 연산자이며 다른 몇몇 이원 연산자에 비해 우선 순위가 높다. 예를 들면

sleep 4 | 3;

은 7초간 sleep 하지 않는다. 우선 4초간 sleep 한 다음, sleep의 반환값(일반적으로 0)과 3과 OR연산한 결과를 반환한다. 따라서 다음과 같이 괄호로 묶은 결과를 수행하게 된다.

(sleep 4) | 3;

아래 예제를 위 sleep의 결과와 비교해 보기 바란다.

print 4 | 3;

위에서는 print하기 이전에 4와 3을 OR 연산한 다음(결과는 7이 된다) 그 결과를 출력한다. 따라서 다음 문장과 동일한 결과를 얻는다.

print (4 | 3);

이것은 print가 단순한 일원 연산자가 아니라 리스트 연산자이기 때문이다. 필요한 경우 괄호를 사용하여 이름 붙은 일원 연산자를 함수로 바꿀 수 있다.

이러한 이름 붙은 일원 연산자에서 특이한 점 중 하나는, 이들 대부분이 변수를 특별히 지정하지 않았을 경우 기본적으로 내장 변수 $_에 대해 해당 연산을 수행한다는 것이다. 그러나 이 때, 연산자 바로 다음에 아래와 같은 변수의 시작부분이 오게 되면 Perl이 해당 문장을 해석하는 데 혼동이 올 수 있다. 만약 변수 다음에 아래와 같은 글자가 오는 경우, Perl의 토큰 분석기는 그것이 용어(term)인지 연산자인지에 따라 서로 다른 타입의 토큰을 반환하게 된다.

글자

연산자

용어

+

더하기

더하기(일원 연산자)

빼기

빼기(일원 연산자)

*

곱하기

*typeglob

/

나누기

/pattern/

보다 작다, 왼쪽으로 쉬프트

<HANDLE>, <<END

.

문자열 연결

.3333

?

?:

?pattern?

%

나머지

%assoc

&

&, &&

&subroutine

따라서 흔히 다음과 같은 실수를 하게 된다.

next if length < 80;

여기서 Perl의 구문 해석기는 <를 연산자로서 “…보다 작다”로 해석하는 것 대신 입력 부호 < >의 시작으로 해석한다. 따라서 괄호 ()를 사용하지 않으면서 명확하게 기본 문자열 변수 $_을 지정하지 않고 원하는 결과를 얻기 위해서는 다음 문장 중 하나를 사용해야 한다.

next if length() < 80;
next if (length) < 80;
next if 80 > length;
next unless length >= 80;

파일 테스트 연산자는 일원 연산자로서 하나의 피연산자를 취하며, 피연산자로 파일명이나 파일 핸들을 취한 다음, 관련된 파일에 대해 아래 표와 같은 사항을 테스트한 결과를 반환한다. 만약 피연산자를 특별히 지정하지 않으면 $_에 대해 테스트한다. (예외로 -t는 STDIN에 대해 테스트한다.) 특별히 언급하지 않은 경우, 테스트한 결과가 참이면 1을, 거짓이면 “”을, 해당 파일이 존재하지 않으면 정의되지 않은 값을 반환한다. 파일 테스트 연산자로는 다음과 같은 것들이 있다.

연산자

-r

파일이 유효 uid/gid 의해 읽기 가능

-w

파일이 유효 uid/gid 의해 쓰기 가능

-x

파일이 유효 uid/gid 의해 실행 가능

-o

파일이 유효 uid/gid 소유

-R

파일이 uid/gid 의해 읽기 가능

-W

파일이 uid/gid 의해 쓰기 가능

-X

파일이 uid/gid 의해 실행 가능

-O

파일이 uid/gid 소유

-e

파일이 존재

-z

파일의 크기가 0

-s

파일의 크기가 0 아님(파일의 크기를 리턴)

-f

보통 파일

-d

디렉토리

-l

심볼릭 링크

-p

이룸 붙은 파이프(FIFO)

-S

소켓

-b

블록 장치

-c

문자 장치

-t

파일 핸들이 tty 열린 상태

-u

setuid 비트가 설정

-g

setgid 비트가 설정

-k

sticky 비트가 설정

-T

텍스트 파일

-B

이진 파일(-T 반대)

-M

가장 최근 변경된 후로부터의 경과 일수

-A

가장 최근 접근된 후로부터의 경과 일수

-C

가장 최근 inode 접근으로부터의 경과 일수

 

-r, -R, -w, -W, -x, -X와 같은 파일 권한에 관한 연산자의 해석은 전적으로 파일의 모드와 해당 사용자의 사용자 ID(uid)및 그룹 ID(gid)에 의존한다. 그러나 파일의 모드나 uid, gid와는 별도로 AFS(Andrew File System)등을 사용하는 시스템에서는 별도의 접근 권한 리스트를 사용하기 때문에 파일로의 접근이 불가능할 경우도 있을 수 있다. 또한, root와 같은 수퍼 사용자의 경우, -r, -R, -w, -W는 항상 1을 반환하고, -x, -X도 해당 파일의 어떤 실행 모드 비트라도 설정되어 있으면 1을 반환한다. 따라서 수퍼 사용자에 의해 실행되는 스크립트에서 실제 파일의 모드를 알아내기 위해서는 stat을 사용하든지 아니면 임시로 uid를 다른 것으로 바꾸어야 한다. 예를 들어

while (<>) {
    chomp;
    next unless -f $_; # ignore “special” files
    …
}

파일 테스트 연산자와 혼동하기 쉬운 다음과 같은 경우 주의해야 할 필요가 있다. 먼저 -s/a/b/는 부정 치환(negated substitution)을 하지 않는다. exp($foo)는 제대로 동작한다. 결론적으로 부호 다음에 하나의 글자만 와야 파일 테스트 연산으로 처리된다.

-T와 -B는 다음과 같은 방식으로 동작한다. 우선 주어진 파일의 처음 한 블록을 읽어 들인 다음에 제어 문자나 최상위 비트(MSB)가 세트되어 있는 글자 등 일반적인 텍스트 외의 글자가 포함되어 있는지 살펴본다. 만약 그런 종류의 문자가 일정 비율(>30%)이상 포함되어 있으면, -B파일로 처리하고, 그렇지 않으면 -T파일로 처리한다. 또한, 첫 번째 블록에 널 문자를 포함하고 있는 파일 역시 -B파일(이진 파일)로 취급한다. 만약 -T나 -B를 파일 핸들에 적용했을 경우, 파일의 첫 번째 블록을 검사하는 것이 아니라 현재의 입력(표준 입출력, “stdio”) 버퍼를 검사한다. 파일 핸들을 테스트하는 경우, 널 파일이거나 파일의 끝(EOF)에 도달하면 -T와 -B 모두 참을 반환한다. -T를 이용하여 파일 테스트를 하기 위해서는 먼저 파일을 읽을 수 있어야 하기 때문에 대개는 다음 예제와 같이 -T 테스트 이전에 -f 테스트를 수행한다.

next unless -f $file && -T _;

밑줄로 표시되는 특별한 형태의 파일 핸들에 파일 테스트 연산자(혹은 stat이나 lstat 연산자)를 적용했을 경우에는, 바로 직전의 파일 테스트(혹은 stat 연산자)의 결과로부터 얻어낸 stat 구조를 사용하기 때문에 시스템 호출을 절약할 수 있다.(이것은 -t의 경우에는 해당되지 않으며, lstat이나 -l의 경우에는 stat 구조에 실제 파일이 아니라 심볼릭 링크에 대한 stat 구조를 남긴다는 것을 주의해야 한다.) 예를 들면,

print “Can do.\n” if -r $a || -w _ || -x _;
stat($filename);
print “Readable\n” if -r _;
print “Writable\n” if -w _;
print “Executable\n” if -x _;
print “Setuid\n” if -u _;
print “Setgid\n” if -g _;
print “Sticky\n” if -k _;
print “Text\n” if -T _;
print “Binary\n” if -B _;

-M, -A, -C 테스트 연산자에 의해 반환되는 값은 스크립트가 실행된 이후부터의 시간을 날짜(소수점 이하 포함)로 표시한 것이다.(스크립트가 실행된 날짜는 특수 변수 $^T 에 저장된다.) 따라서 만약 스크립트가 실행된 후에 파일이 변경된 경우에는 테스트 연산의 결과로 음수가 반환된다. 테스트 결과 반환되는 대부분의 값이 소수점 이하 값을 포함하고 있기 때문에 int 함수를 거치지 않고 정수와 비교하는 것은 의미가 없다. 예를 들면

next unless -M $file > .5; # files older than 12 hours
&newfile if -M $file < 0; # file is newer than process
&mailwarning if int(-A) == 90; # file ($_) accessed 90 days ago today

스크립트의 실행 시작 시간을 현재 시간으로 맞추기 위해서는 다음과 같이 $^T의 값을 변경하면 된다.

$^T = time;

관계 연산자

Perl에는 두 종류의 관계 연산자가 있다. 하나는 숫자 값에 관한 것이고, 다른 하나는 문자열에 관한 연산자이다. 관계 연산자는 아래 표와 같다.

숫자

문자열

gt

크다

>=

ge

크거나 같다

lt

작다

<=

le

작거나 같다

이들 연산자는 참인 경우 1을, 거짓인 경우 “”을 반환한다. 문자열의 비교는 ASCII 문자의 순서에 맞춰 이루어지며, 다른 몇 가지 언어에서와는 달리 문자열 뒤에 붙어 있는 공백 문자도 비교시 고려한다. 관계 연산자는 비결합 연산자이다. 즉, $a < $b < $c와 같은 표현은 문법적 오류를 발생한다.

동등 연산자

동등 연산자는 앞서의 관계 연산자와 비슷하다.

숫자

문자열

==

eq

같다

!=

ne

같지 않다

<=>

cmp

비교한 다음 결과를 부호로 리턴

==와 !=, eqne연산자는 참일 때 1, 거짓일 때 “”을 반환한다. <=>와 cmp 연산자는 왼쪽의 피연산자가 오른쪽 피연산자보다 작을 때는 1을, 같을 때는 0을, 클 때는 +1을 반환한다. 동등 연산자가 관계 연산자와 비슷하게 보이지만, 실제로는 우선 순위가 다르기 때문에 $a < $b <=> $c < $d와 같은 문장은 문법적으로 맞는 문장이다.

비트 연산자

C에서와 마찬가지로 Perl에도 AND, OR, XOR에 해당하는 연산자 &, |, ^를 제공한다. 이 절의 맨 앞쪽의 표에서 보이듯이 이들 중에서는 AND 연산이 가장 우선 순위가 높다. 이들 비트 연산자는 숫자 값에 대해 적용될 때와 문자열에 대해 적용될 때 서로 다르게 동작한다. 두 피연산자 중 어느 한 쪽이라도 숫자 값을 가지면, 두 피연산자 모두 정수로 변환된 다음 두 정수값에 해당하는 비트 연산이 수행된다. 대부분의 컴퓨터에서 이러한 정수는 최소 32비트까지 표현되며, 몇몇 시스템에서는 64비트까지 표현되기도 한다. 중요한 것은 해당 컴퓨터의 구조에 의해 정수로 표시될 수 있는 수의 크기에 제한이 있다는 것이다.

만약 두 개의 피연산자 모두 문자열인 경우에는, 각각에 해당되는 비트별로 비트 연산이 이루어지며, 이 경우 문자열의 길이에는 제한이 없으므로 앞서 정수로 변환될 때와는 달리 그 결과값의 길이에도 제한이 없다. 두 개의 문자열의 길이가 다를 경우, 짧은 쪽 문자열의 맨 뒤에 길이의 차이만큼 0이 붙었다고 간주하고 비트 연산이 행해진다.

예를 들어 두 개의 문자열을 AND 연산하는 경우,

“123.45” & “234.56”

다음과 같은 문자열을 얻는다.

“020.44”

그러나 다음과 같이 문자열과 숫자를 AND 연산하는 경우,

“123.45” & 234.56

다음과 같이 먼저 문자열을 숫자로 변환한 다음

123.45 & 234.56

숫자를 정수로 변환하여

123 & 234

결과적으로 106을 얻게 된다.

모든 비트 문자열은 문자열 “0”을 제외하고는 항상 참임을 주목하기 바란다. 따라서 다음과 같은 형태의 테스트는

if ( “fred” & “\1\2\3\4” ) { … }

다음과 같이 하여야 한다.

if ( (“fred” & “\1\2\3\4”) !~ /^+$/ ) { … }

C 스타일의 논리(단락, short-circuit) 연산자

C언어와 마찬가지로, Perl에서도 &&(논리적 AND)와 ||(논리적 OR)연산자를 제공한다. 이들 논리 연산자는 주어진 문장의 참과 거짓을 판별할 때 왼쪽에서 오른쪽으로(이 때 &&가 ||보다 우선 순위가 높다.) 평가한다. 이들 연산자는 단락(short circuit) 연산자라고도 하는데, 가능한 한 적은 수의 피연산자를 평가하여 주어진 문장의 참과 거짓을 판별해 내기 때문이다. 예를 들어 && 연산자는 좌변의 피연산자가 거짓일 경우, 우변의 피연산자의 값에 관계없이 거짓이기 때문에 실제로는 우변의 피연산자를 평가하지 않는다.

이름

결과

&a && &b

And

$a 참이고 $b 참이면

&a || &b

Or

$a 참이면 $a, 그렇지 않으면 $b

이러한 단락 연산자는 단순히 시간을 절약하기 위해서만이 아니라, 주어진 조건 평가의 흐름을 제어하는 데에도 자주 사용된다. 예를 들어 실제로 많은 Perl 프로그램에서 다음과 비슷한 형태의 문장을 볼 수 있을 것이다.

open(FILE, “somefile”) || die “Cannot open somefile: $!\n”;

이 경우, Perl은 먼저 open 함수를 평가한다. 만약 참이면(즉, somefile이 성공적으로 open되었으면) die 함수를 실행하는 것은 불필요하므로 건너뛴다. 즉 “open somefile or die!”와 같은 문장으로 표현할 수 있다.

||와 && 연산자는 C에서는 0이나 1을 반환하지만, Perl에서는 마지막으로 평가한 값을 반환한다는 점이 다르다. 이것은 참이 될 수 있는 여러 개의 값 중에서 가장 처음으로 참이 되는 값을 선택할 수 있기 때문에 유용하게 사용될 수 있다. 따라서, 사용자의 홈 디렉토리를 찾는 경우 다음과 같이 할 수 있다.

$home = $ENV{HOME}
|| $ENV{LOGDIR}
|| (getpwuid($<))[7]
|| die “You’re homeless!\n”;

Perl은 &&나 ||보다 낮은 우선 순위를 갖는 andor연산자도 제공한다. 이들은 기능적으로는 &&나 ||와 같으나, 좀 더 읽기 쉽고 가능한 한 괄호를 사용하지 않아도 된다는 점이 조금 다르다. andor 역시 단락 연산자이다.

범위 연산자(Range operator)

범위 연산자 “..”는 실제로 연산자가 속해 있는 구문에 따라 두 가지의 서로 다른 연산자로 동작한다. 리스트 구문에서는 범위 연산자의 왼쪽 값부터 오른쪽 값까지 하나씩 증가하는 값의 리스트를 반환한다. 따라서 for (1..10)과 같은 루프나 배열의 일부에 대한 연산의 경우 유용하게 사용된다.

스칼라 구문에서 “..”는 불린 값을 반환한다. .. 연산자는 전기적 플립-플롭(flip-flop)과 같이 두 가지 상태를 가지며, sed, awk, 기타 여러 가지 편집기에서의 행 범위(line-range) 연산자 ,의 기능을 수행한다. 각각의 스칼라 ..연산자는 자신의 불린 상태를 유지한다. .. 연산자의 왼쪽 피연산자가 거짓인 한 거짓을 반환한다. 왼쪽 피연산자가 참이면, 오른쪽 연산자가 참일 때까지 참을 유지하고, 그 후에 범위 연산자는 다시 거짓이 된다. (연산자는 다음 번 평가될 때까지는 거짓이 되지 않는다. 한 번 평가될 때 오른쪽 피연산자를 평가하면서 동시에 거짓이 될 수 있다.(awk의 범위 연산자가 동작하는 방법) 만일 다음 번 평가할 때까지 오른쪽 피연산자를 테스트하고 싶지 않을 경우(sed의 범위 연산자가 동작하는 방법)에는 ..대신 …를 사용하면 된다.) 범위 연산자가 거짓 상태에 있는 동안은 오른쪽 피연산자는 평가되지 않으며, 범위 연산자가 참 상태에 있는 동안은 왼쪽 피연산자는 평가되지 않는다.

범위 연산자는 ||과 &&보다 우선 순위가 낮다. 범위 연산자는 거짓일 경우 널 문자열을 반환하고, 참일 경우는 1부터 시작하는 시퀀스 숫자를 반환한다. 시퀀스 숫자는 매번 새로운 범위를 시작할 때마다 초기화된다. 주어진 영역의 마지막 시퀀스는 해당되는 값 뒤에 문자열 “E0″가 추가되는데, 이렇게 하더라도 해당되는 숫자 값은 변화하지 않지만, 마지막 시퀀스를 알아낼 수 있는 방법을 제공해 주게 된다. 만일 첫 번째 시퀀스를 제외하고 싶으면 시퀀스의 값이 1보다 클 때까지 기다리면 된다. 스칼라 연산에서 ..의 피연산자 중 어느 한 쪽이 숫자 값을 갖는 경우, 해당 피연산자는 입력 파일의 현재 행 번호를 담고 있는 $.변수의 값과 비교하여 평가된다. 예를 들면

if (101 .. 200) { p rint; } # print 2nd hundred lines
next line if (1 .. /^$/); # skip header lines
s/^/> / if (/^$/ .. eof()); # quote body

리스트 구문에서의 연산자로는 다음과 같이 사용된다.

for (101 .. 200) { print; } # prints 101102…199200
@foo = @foo[0 .. $#foo]; # an expensive no-op
@foo = @foo[ -5 .. -1]; # slice last 5 items

리스트 구문에서 범위 연산자는 피연산자가 문자열일 때 자동 증가 알고리듬을 사용한다. 따라서 다음과 같이 하면

@alphabet = (‘A’ .. ‘Z’);

모든 알파벳 글자를 얻을 수 있으며, 다음과 같은 경우

$hexdigit = (0 .. 9, ‘a’ .. ‘f’)[$num & 15];

16진수에 해당하는 한 글자를 얻게 된다. 또한

@z2 = (’01’ .. ’31’); print $z2[$mday];

와 같이 하여 앞에 0이 붙은 날짜를 얻을 수 있으며,

@combos = (‘aa’ .. ‘zz’);

은 영문 소문자 2개로 이루어진 모든 조합을 배열에 대입한다. 그러나 다음과 같은 경우 주의해야 한다.

@bigcombos = (‘aaaaaa’ .. ‘zzzzzz’);

이 경우 실제로 308,915,776 개의 스칼라 값을 저장하기 위한 장소를 필요로 하기 때문에 많은 양의 메모리가 소모된다. 이 때 스왑(swap) 공간이 충분하지 않으면 시스템 오류를 가져올 수도 있다. 이런 경우는 for 문과 같이 반복 구문을 사용해서 처리해야 할 것이다.

조건 연산자

삼원 연산자 ?:는 C에서와 같이 조건 연산자로서 다음과 같이 사용된다.

TEST_EXPR ? IF_TRUE_EXPR : IF_FALSE_EXPR

연산자 ?:는 마치 if-then-else와 비슷한 일을 하지만, 다른 연산이나 함수의 내부에서 안전하게 사용될 수 있다는 점이 다르다. 만약 TEST_EXPR이 참이면, IF_TRUE_EXPR만이 평가되고, 그 결과가 전체 표현식의 값이 된다. TEST_EXPR이 거짓이면, IF_FALSE_EXPR만이 평가되고, 역시 그 결과가 전체 표현식의 값으로 된다.

printf “I have %d dog%s.\n”, $n,

($n == 1) ? “” : “s”;

스칼라 혹은 리스트 어느 것으로 반환이 되는 지는 주어진 구문과 선택된 피연산자에 따라 결정된다.(첫 번째 피연산자는 조건식이므로 항상 스칼라이다.)

$a = $ok ? $b : $c; # get a scalar

@a = $ok ? @b : @c; # get an array

$a = $ok ? @b : @c; # get a count of elements in one of the arrays

두 번째와 세 번째 피연산자 모두 lvalue인 경우에는 다음과 같이 조건 연산자 쪽으로 대입을 할 수도 있다.

($a_or_b ? $a : $b) = $c; # sets either $a or $b to equal $c

대입 연산자

Perl은 C에서의 대입 연산자 외에, 몇몇 C에 없는 대입 연산자도 제공한다. 대입 연산자는 다음과 같은 것들이 있다.

=

**=

+=

*=

&=

<<=

&&=

 

 

-=

/=

|=

>>=

||=

 

 

.=

%=

^=

 

 

 

 

 

x=

 

 

 

각각의 연산자는 좌변에 lvalue(변수나 배열 요소), 우변에 표현식을 필요로 한다. 가장 단순한 대입 연산자인 =의 경우, 우변의 표현식의 결과를 좌변에 지정된 변수에 대입한다. 그 밖의 다른 연산자의 경우, Perl은

$var OP= $value

과 같은 표현식을 다음과 같이 해석하여 실행한다.

$var = $var OP $value

이 때 $var 는 단 한번만 평가된다. 아래 두 연산을 비교해 보자.

$var[$a++] += $value; # $a는 한 번만 증가함
$var[$a++] = $var[$a++] + $value; # $a는 두 번만 증가함

C에서와는 달리, 대입 연산자는 lvalue를 만들어 낸다. 따라서, 대입식 자체를 변경하는 것은 대입을 한 다음 대입된 변수의 값을 변화시키는 것과 같은 결과를 낳는다. 예를 들면

($tmp = $global) += $constant;

은 다음과 같다.

$tmp = $global + $constant;

또한 다음 문장은

($a += 2) *= 3;

다음과 같다.

$a += 2;
$a *= 3;

실제로 아주 유용한 것은 아니지만 때때로 다음과 같은 형태의 문장을 볼 수 있을 것이다.

($new = $old) =~ s/foo/bar/g;

모든 경우, 대입식의 값이 변수의 새로운 값이 된다. 대입 연산자는 오른쪽에서 왼쪽으로 결합하므로, 여러 개의 변수를 같은 값으로 대입하는 경우에 다음과 같이 표시할 수 있다.

$a = $b = $c = 0;

위 식은 $c에 0을 대입하고, 그 결과(0)를 다시 $b에 대입, 그리고 그 결과(여전히 0)를 다시 $a에 대입한다.

리스트 대입은 위의 여러 대입 연산자 중 =연산자만 적용된다. 리스트 구문에서는 스칼라 대입에서와 마찬가지로 대입되는 새로운 값으로 이루어진 리스트가 반환된다. 스칼라 구문에서의 리스트 대입은 앞서 “리스트 값과 배열”에서 설명했듯이, 대입식의 우변에 대입 가능한 값의 개수를 반환한다. 이와 같은 성질 때문에 실패했을 경우 널 리스트를 반환하는 함수의 테스트시 대입 연산자가 유용하게 사용된다. 예를 들면

while (($key, $value) = each %gloss) { … }

next unless ($dev, $ino, $mode) = stat $file;

쉼표 연산자

이진 연산자 “,”는 쉼표 연산자로서, 스칼라 구문에서는 연산자 왼쪽의 피연산자를 평가한 다음 그 결과를 버리고, 오른쪽의 피연산자를 평가하고 그 결과를 반환한다. 이것은 C에서의 쉼표 연산자와 같은 것이다. 예를 들어

$a = (1, 3);

는 $a에 3을 대입한다. 스칼라 구문에서의 쉼표 연산자와 리스트 구문에서의 쉼표 연산자를 혼동해서는 안된다. 리스트에서 쉼표 연산자는 단지 리스트 요소 구분자로서, 좌변과 우변의 피연산자를 모두 LIST에 삽입하며, 어느 값도 버리지 않는다.

예를 들어 위의 예제를 다음과 같이 변경했을 경우,

@a = (1, 3);

이는 두 개의 요소를 갖는 리스트를 만들어 내는 반면,

atan2(1, 3);

은 함수 atan2를 1과 3, 두 개의 피연산자를 갖고 호출하는 것이다.

=>는 쉼표 연산자와 같은 역할을 한다. =>는 리스트에서 키와 값을 쌍으로 묶어서 표기하는 데 유용하게 사용되며, 좌변의 식별자를 강제로 문자열로 해석되게끔 한다.

리스트 연산자 (오른쪽)

리스트 연산자의 우변은 쉼표로 구분되어 있는 모든 리스트 연산자의 피연산자에 해당된다. 따라서, 리스트 연산자의 우선 순위는 쉼표보다 낮다.

논리적 and, or, nor, xor

&&, ||, !와 동일한 기능을 하면서 좀 더 읽기 쉬운 연산자로 and, or, not 연산자가 있다. &&, ||와 마찬가지로 and, or 역시 단락 연산자이다.

이들 연산자의 우선 순위는 매우 낮으나, 리스트 연산자 다음에 사용될 때는 다음과 같이 괄호 없이 사용할 수 있다.

unlink “alpha”, “beta”, “gamma”

or gripe(), next LINE;

C 스타일의 연산자를 사용하면 다음과 같이 나타낼 수 있다.

unlink(“alpha”, “beta”, “gamma”)

|| (gripe(), next LINE);

이 외에 C나 Perl에는 andor등과 같이 대응되는 연산자가 존재하지 않는 연산자인 xor이 있다. 여기서 ^는 피연산자에 비트 단위로 적용되므로 andor등과는 다르다는 것을 주의하기 바란다. 아마 $a xor $b에 해당되는 가장 좋은 대응 표현식은 !$a != !$b일 것이다.[12] 물론 xor 연산자는 양쪽 피연산자 모두를 평가해야 하기 때문에 단락 연산자가 될 수 없다.

Perl에 없는 C 연산자

일원 연산자 &

주소(address)를 알아내는 연산자. Perl에서는 레퍼런스를 취하는 \ 연산자가 비슷한 기능을 담당한다.

$ref_to_var = \$var;

그러나 레퍼런스가 주소를 사용하는 것보다 훨씬 더 안전하다.

일원 연산자 *

주소에 해당되는 값을 알아내는 연산자. Perl에는 주소가 없기 때문에 이같은 연산이 필요없다. Perl에서는 $, @, %, &같은 다양한 형태의 접두 문자(prefix character)가 이와 비슷한 역할을 담당한다. 물론 Perl에도 * 연산자가 존재하나, *는 타입글로브(typeglob) 연산자로서 C에서와 같은 방식으로 사용할 수는 없다.

(TYPE)
타입캐스팅(typecasting) 연산자.

 

문장(Statement)과 선언문(Declaration)

Perl 프로그램은 일련의 선언문과 문장으로 구성되어 있다. 선언문은 문장이 위치할 수 있는 곳이면 어디든지 위치할 수 있으나, 기본적으로 선언문은 프로그램을 컴파일할 때에만 영향을 준다.(일부 선언문은 선언문으로서의 기능 외에 일반적인 문장의 기능을 겸하는 경우도 있다.) sedawk 스크립트에서 매 입력 행에 대해 일련의 문장이 반복해서 실행되는 것과는 달리, Perl 에서는 프로그램이 컴파일 된 후에 프로그램 내의 순서에 따라 일련의 문장이 순서대로 단 한 번 실행된다. 이것은 Perl 프로그램에서는 입력 파일의 모든 행에 대한 작업을 하기 위해서는 명시적으로 루프를 만들어야 하며, 처리하고자 하는 파일이나 행에 대해 프로그램 내에서 구체적인 제어를 해 주어야 한다는 것을 의미한다. 다른 대부분의 고급 언어와는 달리 Perl에서는 서브루틴이나 리포트 포맷문을 만들 때에만 명시적인 선언문이 필요하다. 그 밖에 사용자가 만들어 내는 모든 객체는 처음 생성될 때 대입 연산자를 사용하여 특별히 초기값을 지정하는 경우가 아니면 자동으로 널 이나 0을 값으로 갖게 된다.

물론 필요한 경우 변수를 선언할 수도 있으며, 선언되지 않은 변수를 사용하고자 하는 경우 오류를 발생하도록 할 수도 있다. 이 부분에 대해서는 이 장의 뒤에서 “프라그마(Pragmas)”에 관한 절을 참조하기 바란다.

If 문

if에서 블록은 항상 {}로 묶여 있으므로 ifelse, elsif 등에 각각 해당하는 블록이 무엇인지 명확하게 구분된다. 일련의 if/elsif/else 블록이 있을 경우, 가장 먼저 참 조건을 만족하는 블록만이 실행된다. 만약 참 조건을 만족하는 블록이 없고 else 블록이 있는 경우에는 else 블록이 실행된다.

if대신 unless를 사용하면 조건식의 내용이 반대로 된다. 즉,

unless ($OS_ERROR) …

는 다음과 같다.

if (not $OS_ERROR)

루프

모든 루프에는 부가적으로 LABEL이 올 수 있다. LABEL은 식별자 다음에 콜론(:)이 오는 것으로 만들어지며, Perl의 예약어와 중복되는 것을 피하기 위해 모두 대문자로 사용하는 것이 일반적이다.(물론 LABEL로 BEGIN이나 END를 사용하면 안된다)

While 루프

whileEXPR에 해당하는 조건식이 참인 한, 해당 블록을 반복해서 수행한다. 만약 while대신 until을 사용하는 경우에는 조건식의 내용이 반대가 된다. 즉, 조건식이 거짓인 한 반복해서 수행하게 된다. 루프의 첫 번째 반복 실행(iteration) 이전에도 조건식의 판별은 이루어진다.

while 문의 끝부분에는 부가적으로 continue 블록이 올 수 있다. continue 블록은 바로 앞의 while 블록의 끝에 도달했을 때, 혹은 다음 번 반복 실행으로 가게끔 명시적인 루프 제어 명령이 수행될 때에 실행된다. 실제로 continue 블록은 그렇게 많이 쓰이지는 않는다.

For 루프

C 스타일의 for 루프는 괄호 안에 세미콜론(;)으로 구분되는 세 개의 표현식이 올 수 있다. 세 개의 표현식은 순서대로 각각 초기화, 조건, 재 초기화를 담당한다.(세 개의 표현식 모두 부가적인 것으로 생략할 수 있으며, 만약 조건에 해당하는 부분이 생략되는 경우에는 항상 참인 것으로 간주된다.) for 루프는 다음 예와 같이 while 루프를 사용해 정의할 수 있다.

따라서 아래 for 문은

for ($i = 1; $i < 10; $i++) {
    …
}

다음과 같다.

$i = 1;
while ($i < 10) {
    …
}
continue {
    $i++;
}

for 문에서 두 개의 변수에 대해 동시에 반복 실행하고자 할 경우에는 표현식 사이에 쉼표를 사용하여 구분하면 된다. 예를 들면

for ($i = 0, $bit = 1; $mask & $bit; $i++, $bit << 1) {
    print “Bit $i is set\n”;
}

일반적인 배열의 인덱스에 대해 루프를 구성하는 것 외에도, for 문을 사용하여 다른 형태의 다양한 응용 프로그램을 만들 수 있다. 또한 for 문에서 반드시 루프 변수를 지정해 주어야 하는 것은 아니다. 아래 프로그램은 루프 변수를 지정하지 않은 for 문으로서, STDIN이나 STDOUT과 같은 파일 기술자(file descriptor)의 끝(end-of-file)을 명시적으로 테스트하는 경우 프로그램이 멈추어 버리는 등의 문제를 해결하도록 한 예이다.

$on_a_tty = -t STDIN && -t STDOUT;
sub prompt { print “yes? ” if $on_a_tty }
for ( prompt(); <STDIN>; prompt() ) {
    # do something
}

for 루프의 또 다른 응용으로는 괄호 안의 세 가지 표현식을 모두 생략하는 경우이다. 이런 형태의 문장은 Perl이나 C에서 흔히 볼 수 있는 “무한 루프”를 만드는 방법으로서, 다음과 같다.

for (;;) {
    …
}

비록 for에 해당하는 블록이 무한정 반복 실행되지만, 필요한 경우 루프 제어 명령을 사용하여 언제든지 무한 루프를 빠져 나올 수 있다는 것을 알아 두기 바란다.

Foreach 루프

foreach 루프는 LIST로 지정된 리스트 값에 대해 반복 실행을 하며, 매 루프 실행시 리스트 내의 값을 순서대로 제어 변수(VAR)에 지정한다.

foreach VAR (LIST) {
    …
}

제어 변수는 루프 내에서는 묵시적으로 지역 변수이며, 루프 종료시 이전의 값으로 복구된다. 만약 이전에 my에 의해 전역 변수 대신 사용되었다 하더라도 여전히 루프 내에서는 지역 변수가 된다.

실제로 foreach 키워드는 for 키워드와 동일하다. 따라서 가독성을 위해서는 foreach 를, 간략하게 표기하기 위해서는 for를 사용하면 된다. 만일 VAR가 생략되면, VAR대신 $_가 사용된다. LIST가 배열인 경우(리스트 값을 반환하는 표현식과 반대되는 경우), 루프 내에서 VAR의 값을 변경함으로써 실제 배열의 요소 값을 변경할 수 있다. 이것은 foreach 루프의 루프 제어 변수가 실제로 LIST의 각 리스트 요소와 직접 연결되기 때문이다. 아래 예 중 처음 두 가지는 실제로 배열의 내용을 변경시키는 예제이다.

for (@ary) { s/ham/turkey/ }                          # 치환

foreach $elem (@elements) {                         # 곱하기 2
    $elem *= 2;
}

for $count (10,9,8,7,6,5,4,3,2,1,’BOOM’) {              # 카운트 다운
    print $count, “\n”; sleep(1);
}

for $count (reverse ‘BOOM’, 1..10) {                   # 카운트 다운
    print $count, “\n”; sleep(1);
}

for $item (split /:[\\\n:]*/, $TERMCAP) {          # LIST 표현
    print “Item: $item\n”;
}

foreach $key (sort keys %hash) {                      # keys 정렬
    print “$key => $hash{$key}\n”;
}

위에서 마지막 예는 해쉬의 내용을 출력하는 일반적인 방법이다.

foreach 문에서 현재 리스트의 몇 번째 요소에 대한 처리를 하고 있는 지 알아낼 방법이 없다는 점을 주의해야 한다. 물론, 이전 제어 변수의 값을 기억하고 있다가 비교할 수는 있겠지만, 때때로 일반적인 for 문에서 첨자(subscript)를 사용하여 루프를 만들어야 할 필요도 있게 된다.

아래 예는 Perl에서 C 프로그래머 스타일의 루프 구성의 예를 든 것이다.

for ($i = 0; $i < @ary1; $i++) {
    for ($j = 0; $j < @ary2; $j++) {
        if ($ary1[$i] > $ary2[$j]) {
            last;                                     # 루프 밖으로 나갈 없음 :-(
        }
        $ary1[$i] += $ary2[$j];
    }
}

다음 예는 같은 프로그램을 Perl 프로그래머 스타일로 루프를 구성한 것이다.

WID: foreach $this (@ary1) {
    JET: foreach $that (@ary2) {
        next WID if $this > $that;
        $this += $that;
    }
}

위의 두 프로그램을 비교해 보면 리스트를 이용한 foreach 루프가 훨씬 더 깨끗하고, 안전하고, 빠르다는 것을 알 수 있을 것이다. 같은 일을 하는 데에도 훨씬 간결하게 기술할 수 있다는 점에서 깨끗하다고 할 수 있으며, 안쪽 루프와 바깥쪽 루프 사이에 실수로 실행되지 않을 수도 있는 코드가 없어도 된다는 점에서 안전하다. 또한, for에서는 요소가 첨자를 통해 접근되지만, foreach에서는 각 요소를 직접 접근할 수 있기 때문에 실제로 실행되는 속도가 더 빠르다.

while과 마찬가지로 foreachcontinue 블록을 부가적으로 추가할 수 있다.

루프 제어

앞서 루프의 이름을 LABEL을 사용하여 지정할 수 있다고 언급했었다. 루프의 LABELnext, last, redo와 같은 루프 제어 명령을 사용할 때 해당 루프를 지정하는 데 사용된다. LABEL은 해당 루프의 전체를 의미하는 것이지, 단지 루프의 첫 부분만을 뜻하는 것이 아니다. 따라서, 루프 제어 명령어를 사용할 경우 루프 LABEL이 위치한 곳으로 “go to” 하라는 것을 의미하는 것이 아님을 주의해야 한다.

루프의 이름이 LINE:일 때, 매 행을 처리하는 루프에서 루프 제어 명령을 사용하는 전형적인 예는 다음과 같다.

next LINE if /^#/; # 주석 삭제

루프 제어 명령의 문법은 다음과 같다.

last LABEL
next LABEL
redo LABEL

LABEL은 부가적으로서, 만약 생략된 경우에는 루프 제어 명령은 가장 안쪽 루프에 대해 적용된다. 만일 하나 이상의 루프를 빠져 나오고자 하는 경우에는 반드시 LABEL을 사용해야 한다. 필요에 따라 루프 내에서 얼마든지 루프 제어 명령을 사용할 수 있다.

last 명령은 C에서의 break와 비슷하다. last 명령은 해당 루프를 바로 빠져 나오게 한다. 이 경우 만일 continue 블록이 있더라도 실행되지 않는다. 다음은 첫 번째 행이 비어 있을 경우 루프를 빠져 나오도록 한 예이다.

LINE: while (<STDIN>) {
    last LINE if /^$/; # 빈줄이면 종료
    …
}

next 명령은 C에서의 continue와 비슷하다. next 명령은 루프의 현재 반복을 건너 뛰고 루프의 다음 번 반복을 수행하도록 한다. 이 경우 만약 continue 블록이 있으면 조건식이 평가되기 직전에 항상 수행된다. 따라서 next 명령에 의해 루프의 특정 반복을 건너 뛰게 되었더라도 continue 블록 내에서 루프 변수를 증가시킬 수 있다.

LINE: while (<STDIN>) {
    next LINE if /^#/; # 주석이면 건너뜀
    next LINE if /^$/; # 빈줄이면 건너뜀
    …
} continue {
    $count++;
}

redo 명령은 조건식을 다시 판별하지 않고 루프의 블록을 다시 실행하도록 한다. 이 경우 continue 블록은 실행되지 않는다.

/etc/termcap 파일을 처리하는 경우를 가정하자. 만약 입력된 행이 역슬래쉬로 끝나는 경우, 다음 줄로 이어지는 것으로 해석하여 역슬래쉬를 건너뛰고 다음 레코드로 진행하게 하려면

while (<>) {
    chomp;
    if (s/\\$//) {
        $_ .= <>;
        redo;
    }
    # 내장변수 $_를 처리함
}

위의 프로그램은 다음 프로그램을 간략하게 표기한 것으로서, 두 프로그램은 동일한 기능을 한다.

LINE: while ($line = <ARGV>) {
    chomp($line);
    if ($line =~ s/\\$//) {
        $line .= <ARGV>;
        redo LINE;
    }
    # 변수 $line을 처리함
}

여기서 한 가지 더 짚고 넘어가야 할 점은, 우리가 last, next, redo를 루프 제어 “문” 이 아닌 루프 제어 “명령”으로 취급했다는 것이다. 그 이유는, 이들이 문장처럼 사용될 수 있다 하더라도 실제로 문장은 아니기 때문이다.(C에서 break나 continue가 문장으로만 사용되는 것과 비교) 이들 루프 제어 명령은 제어 흐름을 변경하는 일원 연산자 처럼 생각할 수 있다. 따라서, 이들 루프 제어 명령은 표현식 내에서 아무 곳에서나 사용할 수 있다. 심지어 다음과 같이 오류를 발생시키는 잘못된 형태로도 사용될 수 있다.

open FILE, $file
or warn “Can’t open $file: $!\n”, next FILE; # 잘못된 예

위에서 next FILE은 리스트 연산자 warn의 피연산자로 취급되기 때문에, 실제로 warn이 경고 메시지를 출력하기 전에 next가 수행되게 된다. 이 경우, 다음과 같이 리스트 연산자 warn을 괄호를 사용하여 함수 warn으로 바꾸어 사용하면 된다.

open FILE, $file

or warn(“Can’t open $file: $!\n”), next FILE; # 올바른 예

Bare 블록 과 Case 구조

블록은 그 자체로서는 단 한번 실행되는 루프와 문법적으로 동등하다. 따라서 주어진 블록의 수행을 마치기 위해서 last를, 블록의 처음부터 다시 수행하기 위해서 redo명령을 사용할 수 있다.[16] 이러한 사항이 eval{}, sub{}, do{}에 해당하는 블록에서는 적용되지 않음을 주의해야 한다. 이들은 실제로는 루프 블록이 아니며, 따라서 LABEL을 적용할 수도 없다. 이들은 단지 표현식을 이루는 용어일 뿐이다. 루프 제어 명령은 오로지 루프 내에서만 사용될 수 있다.(return 명령어가 서브루틴이나 eval안에서만 사용될 수 있는 것과 같다.) 물론 필요한 경우 {}를 사용하여 루프와 같은 bare 블록을 만들 수도 있다.

bare 블록은 다음과 같이 다중 스위치(switch) 문에 해당하는 case 구조를 만드는 데 유용하다.

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

Perl에는 스위치 문이 존재하지 않지만, bare 블록과 LABEL, 그리고 루프 제어 명령을 사용하여 이와 같은 효과를 내도록 다양한 방법으로 구현할 수 있다. 위의 예제를 다음과 같이 할 수도 있다.

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

혹은

SWITCH: {
    /^abc/ && do { $abc = 1; last SWITCH; };
    /^def/ && do { $def = 1; last SWITCH; };
    /^xyz/ && do { $xyz = 1; last SWITCH; };
    $nothing = 1;
}

혹은 일반적인 스위치 문과 비슷한 형태를 갖도록 다음과 같이 할 수도 있다.

SWITCH: {
    /^abc/ && do {
        $abc = 1;
        last SWITCH;
    };
    /^def/ && do {
        $def = 1;
        last SWITCH;
    };
    /^xyz/ && do {
        $xyz = 1;
        last SWITCH;
    };
    $nothing = 1;
}

혹은

SWITCH: {
    /^abc/ and $abc = 1, last SWITCH;
    /^def/ and $def = 1, last SWITCH;
    /^xyz/ and $xyz = 1, last SWITCH;
    $nothing = 1;
}

혹은 다음과 같이 다중 if 문장으로 할 수도 있다.

if (/^abc/) { $abc = 1 }
elsif (/^def/) { $def = 1 }
elsif (/^xyz/) { $xyz = 1 }
else { $nothing = 1 }

여러 가지 값에 대해 주어진 블록을 반복 수행하는 일반적인 루프와 달리, 단지 한 값에 대해 루프를 구성하는 것에 대해 의아해 하는 사람도 있을 것이다. 다음 예에서 foreach 문을 사용하여, 긴 이름을 가진 변수와 패턴의 일치를 검색하고자 하는 경우에 변수를 매번 적지 않고 기본 검색 변수 $_를 사용할 수 있다는 것이 한 값에 대해 루프를 구성하는 이유 중의 하나일 것이다.

for ($some_ridiculously_long_variable_name) {
    /In Card Names/ and do { push @flags, ‘-e’; last; };
    /Anywhere/ and do { push @flags, ‘-h’; last; };
    /In Rulings/ and do { last; };
    die “unknown value for form variable where: `$where'”;
}

위의 예에서 앞서 언급한 대로 do{}블록은 루프에 해당되지 않기 때문에 last 명령어에 의해 for 루프를 빠져 나오게 됨을 주목하기 바란다.

Goto

Perl에서도 goto 명령어를 제공한다. goto 명령의 형태는 다음 세 가지가 있다: goto LABEL, goto EXPR, goto &NAME.

goto LABEL이 실행되면, LABEL이라고 이름 붙여진 문장을 찾은 다음 해당 문장으로 프로그램의 수행이 진행된다. 이러한 goto 문은 서브루틴이나 foreach 루프와 같이 초기화가 필요한 구조 안에서는 사용할 수 없다.

goto EXPR은 goto LABEL의 일반적인 형태이다. EXPR의 반환값으로 LABEL을 받으면, 인터프리터가 프로그램 실행 중에 동적으로 해당 LABEL을 해석, 그 곳으로 프로그램의 실행을 진행하는 것이다. 이것은 FORTRAN에서와 같은 계산된(computed) goto 문을 가능하게 해 준다. 그러나 나중에 프로그램의 유지보수 등을 위해 최적화를 고려하는 경우라면 이러한 문장은 가급적 사용하지 않는 편이 좋다.

goto (“FOO”, “BAR”, “GLARCH”)[$i];

대부분의 경우에 goto 문을 사용하는 것 대신 next, last, redo 명령어를 사용한 구조화된 제어 흐름(structured control flow)을 이용하는 편이 훨씬 낫다. 이것은 함수 포인터의 해쉬나 예외 처리(exception handling)를 위한 eval, die 문에서의 catch-throw 방식 등을 사용하는 경우에도 마찬가지로 적용된다.

goto &NAME은 일반적인 goto 문과는 다른 방식으로 동작한다. 이 문장은 현재 실행중인 서브루틴을 위해 NAME으로 이름 붙여진 서브루틴을 호출한다. 이것은 다른 서브루틴을 불러 들이기 위한 AUTOLOAD 문에 의해 사용되며, 마치 다른 서브루틴이 처음으로 수행된 것과 같은 효과를 낸다. 제 7장에서 AutoLoader에 관해 자세히 알아보기로 한다.

전역 선언(Global Declaration)

서브루틴과 포맷 선언은 전역 선언에 해당한다. 전역 선언문은 프로그램 내의 어느 곳에 위치하든지 관계 없으며, 프로그램 어느 곳에서도 적용된다. 이러한 전역 선언문은 일반적인 문장이 올 수 있는 곳이면 어느 곳이나 사용 가능하나, 실제로 프로그램이 순서대로 실행하는 데에는 아무런 영향을 끼치지 않으며 단지 컴파일 시에만 관계한다. 대개 이러한 전역 선언문은 프로그램의 맨 앞이나 맨 뒤에 위치하거나, 별도의 외부 파일에 지정한다.

포맷문은 특정 파일 핸들에 대해 지정되고, 나중에 write 함수에 의해 묵시적으로 사용된다. 포맷문에 대해서는 이 장의 “포맷” 절을 참고하기 바란다.

서브루틴은 실제로 호출되는 지점 이전에 정의될 필요는 없다. 서브루틴 정의(definition)와 선언(declaration)의 차이는, 전자의 경우 서브루틴에서 실제로 실행될 블록을 포함하고 있는 데 비해, 후자는 그렇지 않다는 점이다. 서브루틴 정의는 이전에 서브루틴 선언이 없을 경우 선언문의 기능도 겸하게 된다.

서브루틴을 선언한 이후에는 서브루틴의 이름을 마치 리스트 연산자처럼 사용할 수 있다. 아래 예에서와 같이 서브루틴을 정의하지 않고 선언할 수 있다.

sub myname;
$me = myname $0 or die “can’t get myname”;

서브루틴의 이름을 리스트 연산자처럼 사용할 경우, ||대신 or를 사용하기 바란다. ||은 우선 순위가 높아 리스트 연산자 다음에 사용될 경우 바로 왼쪽의 내용과 결합할 수 있기 때문이다.(리스트 연산자 다음에 괄호를 사용하여 리스트 연산자를 함수 호출로 변환하는 방법도 있다.) 또한 위와 같이 서브루틴을 선언한 다음에는 언젠가 서브루틴의 정의가 있어야 하며, 그렇지 않으면 실행시 정의되지 않은 서브루틴을 호출하였다는 오류 메시지를 받게 된다.

서브루틴 정의는 require를 사용하여 다른 파일에서 불러들일 수도 있다. 그러나 이 경우 다음과 같은 두 가지 문제점이 있을 수 있다. 첫째로, 서브루틴을 호출한 파일의 패키지(이름 영역, namespace)가 아닌 다른 파일이 속해 있는 패키지에 서브루틴의 이름을 삽입하는 것이고, require는 실제 프로그램 실행시 동작하므로 실제로 require문을 실행한 파일 내에서 선언문으로 적용되기에 늦을 수도 있다는 것이다.

선언과 정의를 다른 파일에서 불러 들이는 더 유용한 방법은 use를 사용하는 것이다. use를 사용하면 컴파일 시에 require를 실행하고 현재의 이름 영역에 외부 파일에 선언된 내용을 불러 들일 수 있다. 이 경우 컴파일시 현재의 패키지에 해당 내용을 불러 들인다는 점에서 use의 사용을 전역 선언의 한 가지로 볼 수 있다. 여기에 대해서는 5장에서 자세히 살펴보기로 한다.

범위내 선언(scoped declaration)

전역 선언과 마찬가지로 범위내 선언도 컴파일시에만 효과를 나타낸다. 그러나 범위내 선언은 전역 선언과 달리 선언된 시점 이후의 첫 번재 블록이 끝날 때까지만 유효하다.

앞서 use를 사용할 경우 전역 선언으로 취급된다고 언급했었다. 그러나 use를 사용하여 심볼을 불러 들이는 것 외에, 몇몇 pragmas(프라그마, 컴파일러 힌트)를 구현하는 것도 포함된다. 여기에는 ‘use strict vars’와 같이 범위를 제한하는 프라그마도 포함된다. 자세한 내용은 뒤에 나올 “프라그마(Pragmas)”절을 참고하기 바란다.

package 선언은 패키지가 전역 요소에 해당하는 데에도 불구하고 제한된 범위를 갖는다. package 선언은 선언된 시점부터 해당 블록의 마지막까지 기본 패키지의 이름을 선언하는 것으로, 만약 선언되지 않은 변수를 참조하는 경우 해당 패키지를 참조하도록 하는 것이다.

가장 흔히 볼 수 있는 범위내 선언은 my 변수의 선언일 것이다. 이와 관련된 것으로 동적 범위 제한(dynamic scoping)으로 알려진 local 변수로서, 이름과는 달리 실제로는 전역 변수에 해당한다. 만약 선언되지 않는 어떤 변수의 값을 참조하는 경우, 기본적으로는 프로그램의 모든 곳에서 해당 변수를 참조할 수 있으며(전역 변수), 해당 변수는 프로그램의 종료시까지 유효하다. 프로그램 내의 한 곳에서 사용된 변수는 프로그램의 다른 어느 곳에서도 접근이 가능하다. 만약 Perl에서 이런 경우만 있다면 Perl 프로그램은 크기가 커질수록 다루기 힘들 것이다. 다행히 Perl에서는 my를 이용하여 하위 루틴 안에만 존재하는 변수를, local을 사용하여 메인 프로그램에는 존재하지 않고 하위 루틴과 하위 루틴이 호출하는 다른 하위 루틴 안에 존재하는 변수를 선언할 수 있다. my는 리스트 형태의 변수를, local은 리스트 형태의 전역 변수를 선언하며, 해당 블록이나 서브루틴, eval, 파일 등에 그 범위를 제한한다. 만약 하나 이상의 변수를 나열할 경우 해당 리스트는 괄호로 둘러 싸야 한다. 이 때 리스트에 포함된 모든 요소는 lvalue이어야 한다.(my의 경우는 이보다 좀 더 엄격한 규칙을 따라야 한다. 즉, 모든 요소는 단순한 형태의 스칼라, 배열, 해쉬 변수이어야만 한다.) 여기 몇 가지의 범위내 선언의 예를 들었다.

my $name = “fred”;
my @stuff = (“car”, “house”, “club”);
my ($vehicle, $home, $tool) = @stuff;

(이러한 선언은 실행시 초기화 대입을 수행한다.)

local 변수와 my 변수의 차이점은, local 변수의 경우 해당 변수가 선언된 블록 내에서 호출된 함수에게는 변수가 보이지만, my 변수는 그렇지 않다는 점이다. my 변수는 호출된 서브루틴을 포함해(자기 자신이 자기 자신을 호출했을 때를 포함해서 모든 서브루틴은 자신만의 변수를 따로 갖고 있다.) 밖의 모든 영역에서 해당 변수가 보이지 않는다. local, my 어느 경우에도 프로그램이 mylocal이 해당되었던 영역을 종료하게 되면 해당 변수는 사라지게 된다. 대체로, local 보다는 my가 빠르고 안전하다는 면에서 많이 쓰게 될 것이다. 그러나 만약 이 장의 마지막에서 설명할 특수 변수처럼 기존의 전역 변수의 값을 임시로 변경하고자 하는 경우에는 local을 사용해야만 할 것이다. 여기서는 local에 대해 그리 많은 시간을 할애하지는 않겠다. local에 대한 좀 더 자세한 설명은 3장에서 하기로 하자.

문법적으로는, mylocal은 lvalue 표현식의 단순한 변환자이다. 이러한 대입에서 어떻게 동작하는 지 다음 예를 살펴보자.

my ($foo) = <STDIN>;
my @FOO = <STDIN>;

는 우변에 리스트 구문을 제공하는 반면,

my $foo = <STDIN>;

는 우변에 스칼라 구문을 제공한다.

my는 쉼표 연산자보다 우선 순위가 높으므로 좀 더 결합성이 강하다. 다음 예에서는 단지 한 변수만을 선언하는데, 그 이유는 my 다음에 오는 리스트가 괄호로 둘러 싸여 있지 않기 때문이다.

my $foo, $bar = 1;

위 예제는 다음과 같은 효과를 갖는다.

my $foo;
$bar = 1;

(만약 -w 스위치를 사용했다면 경고 메시지를 받을 것이다.)

선언된 변수는 현재 문장 다음부터 참조될 수 있다. 따라서,

my $x = $x;

와 같이 이전의 밖에 존재했던 $x의 값으로 새로운 $x를 초기화할 수도 있다.(물론 이런 방식으로 사용하는 것을 권하지는 않는다.) 반면에 다음과 같은 경우

my $x = 123 and $x == 123

이전의 $x가 123이라는 값을 갖고 있지 않다면 거짓이 된다.

프라그마(Pragma)

많은 컴퓨터 언어에서는 컴파일러에게 지시할 목적의 문장을 제공하고 있다. Perl에서는 이러한 목적으로 use 선언을 사용한다. 이런 프라그마로는

use integer
use strict
use lib
use sigtrap
use subs
use vars

등이 있다. 이들에 대해서는 7장에서 자세히 다룰 예정이지만 여기에서는 몇 가지에 대해 간단히 알아보기로 한다.

기본적으로 Perl에서는 대부분의 산술 연산이 부동 소수점 연산으로 행해진다. 그러나 다음과 같이 하여

use integer;

지정하게 되면, 컴파일러에게 해당 선언문 이후부터 해당 블록의 마지막까지는 정수 연산으로 하도록 지시하게 된다. 물론 더 안쪽에 위치한 블록에서 다음과 같이 하여

no integer;

정수 연산을 중지하도록 할 수도 있으며, 이 경우 역시 해당 선언문부터 안쪽 블록 마지막까지 유효하게 된다.

use strict ‘vars’;

위 문장은 선언문부터 해당 블록의 마지막까지 변수를 참조하는 경우에 반드시 범위내 지정된 변수를 사용하거나, 패키지 이름을 포함한 변수 이름을 사용하도록 강제적으로 지시한다. 그렇지 않을 경우 컴파일시 오류를 발생한다. 위 문장과 반대되는 문장은 다음과 같다.

no strict ‘vars’

또한 이런 pragma를 사용하여 심볼 레퍼런스나 bareword의 체크를 좀 더 엄격하게 하도록 할 수 있다. use strict; 문장을 사용하면 변수를 포함해 이들 세 가지 항목에 대해 컴파일시 엄격한 체크를 수행한다.

다른 모듈로부터 불러 들인 서브루틴이나 변수는 Perl에서 특별한 권한을 갖는다. 불러 들인 서브루틴은 기존의 연산자를 겹쳐 쓸 수 있으며, 불러 들인 변수는 use strict ‘vars’의 적용을 받지 않는다. 때때로 사용자가 만든 서브루틴이나 변수에 대해서도 이러한 권한을 부여하고 싶을 때가 있는데 이 경우 다음과 같이 한다.

use subs qw(&read &write);
use vars qw($fee $fie $foe $foo @sic);

마지막으로, Perl은 모듈을 기본적으로 주어진 위치에서 찾는다. use 명령어로 모듈을 참조할 경우 컴파일시에 해당 모듈을 추가하여야 하므로, 프로그램의 앞쪽에 다음과 같이 하여 모듈의 위치를 지정해 줄 수 있다.

use lib “/my/own/lib/directory”;

앞서 프라그마 중에서 마지막 세 가지는 전역 구조(global structure)를 변화시키는 것으로서, 현재 범위 밖에서도 적용된다는 점을 주의하기 바란다.

서브루틴

다른 언어와 마찬가지로 Perl에서도 사용자가 정의한 서브루틴을 제공한다. 이러한 서브루틴은 주 프로그램 내의 어느 곳에서도 정의될 수 있으며, do, require, use 키워드를 사용하여 다른 파일에서부터 불러 들일 수도 있고, eval을 사용하여 프로그램 수행 중에 만들어 낼 수도 있다. 또한 레퍼런스를 통해서만 접근할 수 있는 익명(anonymous) 서브루틴을 만들 수도 있다. 그리고 심지어는 서브루틴의 이름이나 서브루틴의 레퍼런스를 담고 있는 변수를 통해 간접적으로 서브루틴을 호출할 수도 있다.

서브루틴을 선언하려면 다음 중 한 형식을 따르면 된다.

sub NAME; # 서브루틴 선언
sub NAME (PROTO); # 위와 같지만 원형이 있음

서브루틴을 선언하고 정의하려면 다음 중 한 형식을 따른다.

sub NAME BLOCK # 서브루틴 선인 및 정의
sub NAME (PROTO) BLOCK # 위와 같지만 원형이 있음

실행시 익명 서브루틴을 정의하려면 다음과 같은 형식의 문장을 사용한다.

$subref = sub BLOCK;

다른 패키지에서 정의된 서브루틴을 읽어 들이려면

use PACKAGE qw(NAME1 NAME2 NAME3 …);

서브루틴을 직접 호출하려면

NAME(LIST); # & is optional with parentheses.
NAME LIST; # Parens optional if predeclared/imported.
&NAME; # Passes current @_ to subroutine.

이름이나 레퍼런스를 통해 서브루틴을 간접 호출하려면

&$subref(LIST); # & is not optional on indirect call.
&$subref; # Passes current @_ to subroutine.

Perl에서 서브루틴으로 보내지는 데이터와 서브루틴에서 반환되는 데이터를 전달하는 방법은 간단하다. 모든 파라미터는 하나의 스칼라 리스트로 전해지며, 반환되는 값도 마찬가지 형태로 전달된다. LIST에 포함된 배열이나 해쉬는 하나의 스칼라 리스트로 만들어질 때 치환 과정을 겪는다.

Perl에서는 서브루틴으로 전달되는 파라미터를 @_라는 배열에 저장하여 전달한다. 만약 서브루틴을 두 개의 파라미터를 갖고 호출한 경우, 두 개의 파라미터는 각각 $_[0]과 $_[1]에 저장된다. @_역시 배열이므로, 파라미터 리스트에도 배열에 관한 여러가지 연산을 그대로 수행할 수 있다. 배열 @_는 지역 배열이지만, 그 값은 실제 스칼라의 묵시적 레퍼런스 값에 해당한다. 따라서 배열 @_의 요소값을 변경하여 실제 전달되는 파라미터의 값을 변경할 수 있다.

특별히 지정하지 않는 경우, 서브루틴의 반환값으로는 가장 마지막에 평가된 표현식의 값이 된다. 또는 return으로 특별히 서브루틴의 반환값을 지정하고 서브루틴을 종료하도록 할 수도 있다. 어느 경우에도, 해당 서브루틴이 호출된 지점이 스칼라 구문인지, 리스트 구문인지에 따라 반환값도 같은 방식으로 처리된다.

Perl에서는 이름붙은 정규(formal) 파라미터를 따로 갖고 있지 않으므로, 실제로는 @_의 내용을 서브루틴 내에서 my 변수 리스트에 대입하여 파라미터를 따로따로 사용할 수 있다. 만약 my 변수 리스트 대입과정이 없는 경우 전체 배열 @_에 대해 처리해야 한다.

sub max {
    my $max = shift(@_);
    foreach $foo (@_) {
        $max = $foo if $max < $foo;
    }
    return $max;
}
$bestday = max($mon,$tue,$wed,$thu,$fri);

다음 예는 전역 lookahead 변수를 유지하기 위해 전달되는 파라미터 전체를 무시하는 경우이다.

# Get a line, combining continuation lines that start with whitespace
sub get_line {
    my $thisline = $LOOKAHEAD;
    LINE: while ($LOOKAHEAD = <STDIN>) {
        if ($LOOKAHEAD =~ /^[ \t]/) {
            $thisline .= $LOOKAHEAD;
        }
        else {
            last LINE;
        }
    }
$thisline;
}
$LOOKAHEAD = <STDIN>; # get first line
while ($_ = get_line()) {
    …
}

정규 파라미터의 이름을 지정하기 위해서는 다음과 같이 리스트 대입을 한다.

sub maybeset {
    my($key, $value) = @_;
    $Foo{$key} = $value unless $Foo{$key};
}

위와 같이 하여, 전산학에서 말하는 레퍼런스에 의한 호출(call-by-reference)을 값에 의한 호출(call-by-values) 형태로 바꾸는 효과를 얻는다.

다음은 정규 파라미터를 특별히 지정하지 않는 예로, 실제 전달되는 파라미터의 값을 바꿀 수 있다.

upcase_in($v1, $v2); # $v1와 $v2를 바꿈
sub upcase_in {
    for (@_) { tr/a-z/A-Z/ }
}

물론 이러한 방법으로 상수값을 바꿀 수는 없다. 만약 전달되는 값이 실제로 문자 상수이고 이러한 방법으로 그 값을 바꾸려고 하는 경우 예외처리(exception)를 겪게 될 것이다. 예를 들어 다음과 같은 식으로는 동작하지 않는다.

upcase_in(“frederick”);

다음과 같이 upcase_in() 함수가 파라미터를 직접 바꾸지 않고 복사된 파라미터를 반환하도록 하는 것이 훨씬 안전할 것이다.

($v3, $v4) = upcase($v1, $v2);
sub upcase {
    my @parms = @_;
    for (@parms) { tr/a-z/A-Z/ }
    # wantarray checks whether we were called in LIST context
    return wantarray ? @parms : $parms[0];
}

Perl에서는 파라미터를 하나의 리스트 @_로 전달받기 때문에 실제로 전달된 파라미터가 스칼라인지 배열인지 관계하지 않는다. 이것이 Perl에서 말하는 단순한 형태의 파라미터 전달 형식이다. upcase 함수는 함수 자체의 정의를 바꾸지 않고서도 다음과 같이 여러 가지 형태로 사용될 수 있다.

@newLIST = upcase(@LIST1, @LIST2);
@newLIST = upcase( split /:/, $var );

그러나 다음과 같이는 되지 않는다.

(@a, @b) = upcase(@LIST1, @LIST2); # 틀린 예

왜냐하면, 서브루틴으로 전달되는 파라미터 리스트와 마찬가지로, 반환되는 리스트 역시 평평한 구조의 하나의 크고 긴 리스트이기 때문이다. 따라서 위에서는 @a에 모든 값이 저장되고 @b는 빈 리스트가 된다. 다음 “레퍼런스 전달” 절에서 다른 경우에 대해 살펴보기로 하자.

서브루틴의 이름 앞에는 &를 써야 한다. 그러나 서브루틴을 호출할 때 &는 붙이지 않아도 되며, 만약 서브루틴이 이전에 미리 선언된 경우에는 괄호도 생략할 수 있다.(그러나, definedundef 등의 파라미터로 서브루틴의 이름을 사용하는 등의 경우에는 & 를 생략할 수 없으며, $subref = \&name 등과 같이 서브루틴의 이름을 지정하여 레퍼런스를 만들어 내는 경우에도 &를 생략할 수 없다. 또한 &$subref()나 &{$subref}()등의 구조에서와 같이 서브루틴의 이름이나 레퍼런스를 통해 간접 호출하는 경우에도 &를 생략할 수 없다. 여기에 대해서는 4장에서 자세히 다룰 예정이다.)

서브루틴은 재귀적(recursively)으로 호출될 수 있다. 만약 서브루틴이 &를 사용한 형태로 호출될 경우 파라미터 리스트는 부가적으로서, 만약 생략될 경우에는 배열 @_도 서브루틴에 전달되지 않는다. 이 경우 대신 호출한 시점에서 호출한 서브루틴의 배열 @_이 호출된 서브루틴에서도 접근 가능하다.

&foo(1,2,3); # pass three arguments
foo(1,2,3); # the same
foo(); # pass a null LIST
&foo(); # the same
&foo; # foo() gets current args, like foo(@_) !!
foo; # like foo() if sub foo pre-declared, else bareword “foo”

&를 사용하면 전달되는 파라미터 리스트를 부가적인 것으로 할 수 있을 뿐 아니라, 주어지는 파라미터에 대한 프로토타입 체크를 하지 않게도 한다. 여기에 대해서는 이 장의 “프로토타입” 절을 참조하기 바란다.

함수 내부에서 my로 선언되지 않은 임의의 변수는 전역 변수에 해당한다. 하위루틴에만 변수를 생성하는 것에 관해서는 3장의 my 항목을 참조하기 바란다.

심볼 테이블 내역(Symbol Table Entry) 전달

이 절에서 설명할 방식은 이전 버전의 Perl에서는 레퍼런스에 의한 전달을 구현하는 유일한 방법이었다. 물론 현재 버전의 Perl에서도 무리 없이 동작하지만, 새로 도입된 레퍼런스 방식에 의해 이러한 점을 모두 구현할 수 있으며 사용하기에도 훨씬 편하다. 이에 대해서는 앞으로 설명할 것이다.

때때로 서브루틴에 배열의 값을 전달하지 않고 배열의 이름 자체를 전달하여, 해당 서브루틴으로 하여금 배열의 지역 복사본(local copy) 대신 전역 복사본(global copy)의 내용을 변경할 수 있게끔 하고자 할 때가 있다. Perl에서는 특정한 이름을 갖는 모든 객체를 이름 앞에 *를 붙여 참조할 수 있다. 예를 들면 *foo와 같다. 이것은 타입글로브(typeglob)라고 하는 것으로서, 이름 앞에 붙은 *가 일종의 와일드카드(wildcard)로 동작한다고 보면 된다.

평가될 때, 타입글로브는 그 이름에 해당하는, 임의의 스칼라, 배열, 해쉬 변수, 파일 핸들, 포맷, 서브루틴 등을 포함한 모든 객체를 대표하는 스칼라 값을 만들어 낸다.

sub doubleary {
    local(*someary) = @_;
    foreach $elem (@someary) {
        $elem *= 2;
    }
}
doubleary(*foo);
doubleary(*bar);

스칼라는 이미 레퍼런스에 의한 전달 방식을 사용하므로, 위와 같은 방식을 사용하지 않고 명시적으로 $_[0]등에 의해 스칼라 파라미터를 변경할 수 있다. 또한, 배열의 모든 요소를 스칼라로 전달하여 그 값을 변경할 수 있다. 그러나 push, pop, 혹은 배열의 크기를 변화하고자 하는 경우에는 위와 같은 * 방식이나 다음에 설명할 레퍼런스 방식을 사용해야 한다. 물론 전체 배열을 스택에 넣었다가 차례차례 참조하는 방식의 파라미터 전달보다 타입글로브를 이용하는 편이 속도 면에서 훨씬 유리하다.

만약 배열을 변경시키지 않는 경우에도, 이런 방식은 하나의 LIST에 여러 개의 배열을 전달하는 경우에 유용하다. 왜냐하면 일반적인 LIST 방식에서는 모든 리스트 값을 동일한 하나의 리스트로 만들어 취급하므로 나중에 각각의 배열을 추출할 수 없기 때문이다.

레퍼런스 전달

만약 하나 이상의 배열이나 해쉬를 서브루틴에 전달하거나, 서브루틴의 반환값으로 전달하고 싶을 때는 다음에 설명할 레퍼런스에 의한 전달 방식을 사용해야 할 것이다. 이러한 레퍼런스 전달에 대해서는 4장에서 자세히 다룰 예정이다. 여기서는 레퍼런스 전달에 대해 간략히 소개하도록 하겠다.

여기 몇 가지 간단한 예가 있다. 첫 번째는, 몇 개의 배열을 한 함수로 전달하여, 함수로 하여금 각각을 pop한 다음, 마지막 요소 값들로 이루어진 새로운 리스트를 반환하도록 한다.

@tailings = popmany ( \@a, \@b, \@c, \@d );
sub popmany {
    my $aref;
    my @retLIST = ();
    foreach $aref ( @_ ) {
        push @retLIST, pop @$aref;
    }
    return @retLIST;
}

다음은 전달된 모든 해쉬의 키로 이루어진 리스트를 반환하는 함수의 예이다.

@common = inter( \%foo, \%bar, \%joe );
sub inter {
    my ($k, $href, %seen); # 지역변수
    foreach $href (@_) {
        while ( ($k) = each %$href ) {
            $seen{$k}++;
        }
    }
    return grep { $seen{$_} == @_ } keys %seen;
}

지금까지는 일반적인 리스트 반환방식을 사용했다. 만약 해쉬를 전달하거나 반환하고자 하는 경우에는 어떻게 동작할 것인가?

많은 경우 다음과 같이 하면 문제가 발생한다.

(@a, @b) = func(@c, @d);

혹은

(%a, %b) = func(%c, %d);

위에서는 @a나 %a는 func()의 반환값으로 지정되나 @b, %b는 단순히 초기화되는 것에 그친다. 또한 func() 함수 자체도 두 개의 분리된 배열이나 해쉬를 전달 받는 것이 아니라 하나의 긴 리스트 @_를 받는다.

따라서 위와 같은 경우 함수를 만들 경우 파라미터와 반환으로 레퍼런스를 전달하도록 하면 훨씬 간단한 형태로 코드를 만들어 낼 수 있다. 다음은 두 개의 배열을 파라미터로 전달 받아 얼마나 많은 요소 값을 갖고 있는가에 따라 순서를 매긴 두 개의 배열에 대한 레퍼런스를 반환하는 함수의 예이다.

($aref, $bref) = func(\@c, \@d);
print “@$aref has more than @$bref\n”;
sub func {
    my ($cref, $dref) = @_;
    if (@$cref > @$dref) {
        return ($cref, $dref);
    } else {
        return ($dref, $cref);
    }
}

다음과 같이 타입글로브를 레퍼런스와 함께 사용할 수도 있다.

(*a, *b) = func(\@c, \@d);
print “@a has more than @b\n”;
sub func {
    local (*c, *d) = @_;
    if (@c > @d) {
        return (\@c, \@d);
    } else {
        return (\@d, \@c);
    }
}

파일 핸들을 전달하는 경우에는 *STDOUT과 같이 bare 타입글로브를 사용하면 되나, 타입글로브에 대한 레퍼런스를 사용하면 use strict ‘refs’와 같은 상황에서도 정상적으로 동작하기 때문에 더 낫다고 할 수 있다. 예를 들면,

splutter(\*STDOUT);
sub splutter {
    my $fh = shift;
    print $fh “her um well a hmmm\n”;
}
$rec = get_rec(\*STDIN);
sub get_rec {
    my $fh = shift;
    return scalar <$fh>;
}

만일 새로운 파일 핸들을 만들어 내는 경우에는 3장의 open 항에 있는 파일핸들(FileHandle) 모듈을 이용하는 예를 참고하기 바란다.

프로토타입(Prototype, 원형)

5.003 버전의 Perl에서는 사용자가 만든 서브루틴에 대해서 Perl 기본 함수와 마찬가지로 파라미터의 숫자나 타입에 대한 제한을 할 수 있게 되었다. 예를 들어 다음과 같이 선언했을 경우,

sub mypush (\@@)

mypush는 정확히 push가 취하는 것과 같은 방식으로 파라미터를 취한다. 나중에 호출될 함수의 선언은 컴파일할 때 준비되어 있어야 한다. 이러한 프로토타입은 함수 호출시 &를 사용하지 않는 새로운 스타일의 함수 호출에서만 적용된다. 또한 이런 프로토타입은 서브루틴의 레퍼런스 \&foo나 간접 서브루틴 호출 &{$subref}등에는 전혀 영향을 끼치지 않는다.

이러한 프로토타입은 기본적으로 사용자로 하여금 기본 내장된 명령어(또는 함수) 와 비슷하게 동작하는 서브루틴을 정의할 수 있도록 해 주는 것으로서, 아래 표는 몇몇 함수와 그에 해당되는 기본 내장 명령어를 표기한 것이다.(아래 표에서 “my”는 Perl에서의 my 연산자와는 관계 없는 것으로서, 사용자가 만든 서브루틴임을 표시하기 위해서 붙인 것이다.)

선언

호출

sub mylink ($$)

mylink $old, $new

sub myvec ($$$)

myvec $var, $offset, 1

sub myindex ($$;$)

myindex &getstring, “substr”

sub mysyswrite ($$$;$)

mysyswrite $buf, 0, length($buf) $off, $off

sub myreverse (@)

myreverse $a, $b, $c

sub myjoin ($@)

myjoin “:”, $a, $b, $c

sub mypop (\@)

mypop @array

sub mysplice (\@$$@)

mysplice @array, @array, 0, @pushme

sub mykeys (\%)

mykeys %(hashref)

sub myopen (*;$)

myopen HANDLE, $name

sub mypipe (**)

mypipe READHANDLE, WRITEHANDLE

sub mygrep (&@)

mygrep { /foo/ } $a, $b, $c

sub myrand ($)

myrand 42

sub mytime ()

mytime

 

역슬래쉬가 붙은 프로토타입 문자는 실제로 해당 서브루틴의 파라미터로 사용될 때는 반드시 해당 문자로 시작되어야 한다는 뜻이다. 예를 들어 keys의 첫 번째 파라미터는 항상 %로 시작하는 것과 같이, mykeys의 파라미터도 %로 시작해야 한다는 것이다.

역슬래쉬가 붙지 않은 프로토타입 문자는 특별한 의미를 갖는다. @나 %는 나머지 모든 파라미터를 포함하여 리스트 구문을 만든다.(문법 표현에서의 LIST와 동일하다.) $로 표시되는 파라미터는 스칼라 구문을 만든다. &는 익명 서브루틴을 필요로 한다.(즉, 만약 첫 번째 파라미터로 전달되는 경우, sub 키워드나 뒤따라오는 쉼표 연산자를 요구하지 않는다.) 그리고 *는 해당 파라미터가 무엇이든 심볼 테이블의 레퍼런스로 변환한다. 이것은 대개 파일 핸들로 사용된다.

세미콜론은 반드시 필요한 파라미터와 부가적인 파라미터를 분리하는 역할을 한다.(@ 나 %앞의 세미콜론은 있으나 없으나 같다. 리스트는 널(Null)일 수 있기 때문이다.)

마지막 세 가지 예가 다른 예와 달리 어떻게 특별히 취급되는지 주목하기 바란다. mygrep은 리스트 연산자로 취급되며, myrand는 일원 연산자로 rand와 같은 연산 우선 순위를 가지며, mytime은 time과 마찬가지로 파라미터를 필요로 하지 않는다.

즉 다음과 같은 경우,

mytime +2;

mytime() + 2를 얻게 되는 것이지, mytime(2)를 얻는 것은 아니라는 뜻이다. 즉, 위와 같은 프로토타입에 따른 연산의 우선 순위에 의해 해석이 결정되는 것이다.

다음은 grep 연산자를 다른 형태로 구현한 것이다.(물론 기본 내장된 grep이 훨씬 효율적이다.)

sub mygrep (&@) {
    my $coderef = shift;
    my @result;
    foreach $_ (@_) {
        push(@result, $_) if &$coderef;
    }
    @result;
}

포맷 (Format)

Perl에서는 단순한 형태의 포맷을 갖춘 리포트나 차트를 쉽게 만들 수 있게 하는 방법을 제공한다. 이러한 포맷문은 쪽 당 줄 수나 현재 쪽 번호, 언제 머리글을 출력할 것인지 등의 정보도 제공한다. 포맷문의 키워드는 포트란(FORTRAN)에서 빌려온 것이다:포맷을 선언할 때는 format을, 해당 포맷을 출력할 때는 write를 사용한다. 이들과 관련된 항목에 대해서는 3장을 참조하기 바란다. 다행스럽게도 Perl에서의 포맷문의 레이아웃은 비교적 읽기 쉬운 형태로, 마치 BASIC의 PRINT USING과 비슷한 형태를 갖는다. 포맷문을 ‘가난한 자의 nroff(1)‘이라고 부르는 사람도 있다.

패키지나 서브루틴과 마찬가지로, 포맷문 역시 실행되는 것이 아니라 선언문이므로 프로그램의 어느 곳에나 위치할 수 있다.(실제로는 이러한 포맷문을 프로그램의 한 곳에 모아 두는 것이 여러모로 편리하다.) Perl 에서의 포맷문은 다른 모든 타입과는 별개의 이름 영역(namespace)를 갖는다. 즉, 만약 “Foo”라는 함수가 존재하더라도 “Foo”라고 이름 붙은 포맷과는 별개라는 것이다. 그러나 주어진 파일 핸들에 대한 기본 포맷 이름은 파일 핸들의 이름과 같다. 따라서 STDOUT의 기본 포맷 이름은 “STDOUT”이고, 파일 핸들 TEMP의 기본 포맷 이름은 “TEMP”가 된다.

출력 레코드 포맷은 다음과 같이 선언된다.

format NAME =

FORMLIST

.

만일 NAME이 생략되면, STDOUT에 대한 포맷이 정의된다. FORMLIST는 다음 3 가지 중 한 형식을 따르는 여러 행으로 구성된다.

첫 번째 컬럼이 #로 시작하는 주석문(comment)

출력될 행의 포맷을 정의하는 줄

이전 행에 연결되는 값을 제공하는 줄

출력될 행의 포맷을 정의하는 줄은 어떤 값을 대입하도록 지시하는 특정한 필드를 제외하고는 보이는 대로 출력된다. 각각의 대입 필드(substitution field)는 @나 ^로 시작된다. 이들 행에서는 어떤 변수 치환도 이루어지지 않는다. 필드 @(배열 변수의 @와 혼동하지 말 것)는 가장 일반적인 형태의 필드이다. 반면 ^ 필드는 여러 줄에 걸친 텍스트 블록 채우기를 지정하는 필드이다. 필드의 길이는 @나 ^ 다음에 여러 개의 <, >, |를 개수 만큼 덧붙여서 지정하며, <는 왼쪽 정렬, >는 오른쪽 정렬, |는 가운데 정렬을 의미한다. 만일 지정된 필드의 폭을 초과하는 변수에 대해서는 초과하는 만큼 버린 값이 출력된다.

오른쪽 정렬의 다른 형태로, 숫자 값을 지정하기 위해서는 @나 ^다음에 숫자 개수 만큼 #를 사용하면 되며, 소수점을 나타내기 위해서 .를 사용할 수 있다. 이러한 필드에 해당되는 값에 개행문자(newline)가 포함되었을 경우에는 개행문자 앞까지의 내용이 출력된다. 마지막으로 특수 필드 @*는 변수의 뒷부분을 버리지 않고 여러 줄에 걸쳐 출력됨을 의미한다.

이전 행에 연결되는 값을 제공하는 줄에서, 값들은 이전 행에서 지정된 필드의 순서대로 연결된다. 값을 제공하는 표현식들은 쉼표로 구분되어야 한다. 표현식은 모두 해당 행이 처리되기 전에 리스트에서 평가되므로, 하나의 리스트 표현식이 여러 개의 리스트 요소를 만들어 낼 수 있다. 표현식은 {}로 둘러 싸인 경우 여러 줄에 걸쳐 표기할 수 있으며, 이 경우 {는 반드시 첫 번째 행의 첫 번째 토큰이어야 한다.

^로 시작하는 필드는 @과는 달리 특별하게 취급된다. #필드와 함께 사용되는 경우, ^필드는 해당되는 값이 정의되지 않았을 때 비워 놓는다. 다른 필드 타입의 경우, ^는 채우기 모드로 동작한다. 임의의 표현식 대신, 제공되는 값은 반드시 텍스트 문자열을 포함하는 스칼라 변수 이름이어야 한다. Perl은 해당 필드에 가능한 한 많은 텍스트를 출력하고, 해당 문자열에서 출력된 만큼 앞부분을 잘라내어 다음에 해당 변수가 참조될 때 나머지 텍스트를 출력할 수 있게끔 한다.(즉, 이것은 write를 호출하여 실행시에 해당 변수의 내용이 변경된다는 것을 의미한다. 만약 원래의 값을 보존하고 싶을 경우에는 스크래치(scratch) 변수를 사용하기 바란다.) 보통 텍스트 블록을 출력하기 위해서는 수직으로 여러 필드를 사용한다. 텍스트를 표시하기에 너무 긴 경우 마지막 필드를 “…”로 지정하여 표시하도록 할 수 있다. 변수 $:의 값을 변경하여 개행문자를 개행문자외의 다른 문자로 변경할 수 있다.

^ 필드를 사용하여 길이가 가변적인 레코드를 사용할 수 있다. 포맷될 텍스트가 짧은 경우, 단순히 ^필드를 포함한 포맷 행을 몇 번 반복하면 된다. 이 경우 짧은 길이의 데이터를 출력하게 되면 뒷 쪽의 몇 행은 비어 있는 행으로 출력된다. 이렇게 비어 있는 행이 출력되는 것을 방지하기 위해서는 행의 임의의 장소에 ~ 문자를 지정하여야 한다. 만약 첫 번째 ~ 바로 뒤에 두 번째 ~를 연이어 지정할 경우, 해당 행의 필드에 모든 텍스트가 출력될 때까지 해당 줄이 반복된다.

머리글은 기본적으로 포맷의 이름에 “_TOP”을 붙인 이름으로 지정된다. 머리글은 매 페이지의 윗 부분에 매번 적용된다. 3장의 write 항을 참조하기 바란다.

포맷문의 예는 다음과 같다.

# /etc/passwd               파일에 대한 리포트
format STDOUT_TOP =
                    Passwd File
Name                Login    Office   Uid   Gid Home
—————————————————————
.
format STDOUT =
@<<<<<<<<<<<<<<<<<< @||||||| @<<<<<<@>>>> @>>>> @<<<<<<<<<<<<<<<<<
$name,              $login,  $office,$uid,$gid, $home
.
 

# 버그 리포트 양식
format STDOUT_TOP =
                         Bug Reports
@<<<<<<<<<<<<<<<<<<<<<<<     @|||         @>>>>>>>>>>>>>>>>>>>>>>>
$system,                         $%,         $date
—————————————————————
.
format STDOUT =
Subject: @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
        $subject
Index: @<<<<<<<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
      $index,                        $description
Priority: @<<<<<<<<<< Date: @<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
        $priority,          $date,    $description
From: @<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
      $from,                          $description
Assigned to: @<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            $programmer,            $description
~                                   ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                                    $description
~                                   ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                                    $description
~                                   ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                                    $description
~                                   ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                                    $description
~                                   ^<<<<<<<<<<<<<<<<<<<<<<<…
                                    $description
.

같은 출력 채널(파일 핸들 등)에 printwrite를 동시에 사용할 수도 있으나, 이 경우 특수 변수 $-(English 모듈을 사용할 경우 $FORMAT_LINES_LEFT)의 값을 직접 처리해야 한다.

포맷 변수

현재 포맷의 이름은 변수 $~($FORMAT_NAME)에 저장되며, 현재 머리글 포맷 이름은 $^($FORMAT_TOP_NAME)에 저장된다. 현재 출력 쪽 수는 $%($FORMAT_PAGE_NUMBER)에, 쪽 당 줄 수는 $=($FORMAT_LINES_PER_PAGE)에 저장된다. 해당 핸들의 출력을 자동으로 비울것인지의 여부는 $|($OUTPUT_AUTOFLUSH)에 저장된다. 매 쪽의 머리글 전에 출력될 문자열(첫 번째 쪽만 제외), 즉 쪽 넘김,은 $^L($FORMAT_FORMFEED)에 저장된다. 이들 변수는 파일 핸들마다 각각 설정할 수 있으므로, select로 원하는 파일 핸들을 지정한 다음 해당 변수의 내용을 변경하여야 한다.

select((select(OUTF), $~ = “My_Other_Format”, $^ = “My_Top_Format”)[0]);

필요한 경우 다음과 같이 이전 파일 핸들을 저장한 다음 포맷 처리가 끝난 후 복구하는 방법도 있다.

$ofh = select(OUTF);
$~ = “My_Other_Format”;
$^ = “My_Top_Format”;
select($ofh);

English 모듈을 사용하면 다음과 같이 변수의 이름을 읽을 수 있는 형태로 지정할 수 있다.

$ofh = select(OUTF);
$FORMAT_NAME = “My_Other_Format”;
$FORMAT_TOP_NAME = “My_Top_Format”;
select($ofh);

select 문과 특수 변수의 값을 바꾸는 것 대신, 파일핸들(FileHandle) 모듈을 사용하여 파일 핸들의 메쏘드를 호출하는 것으로 같은 효과를 얻을 수 있다.

use FileHandle;
OUTF->format_name(“My_Other_Format”);
OUTF->format_top_name(“My_Top_Format”);

이전 행에 연결되는 값을 포함하는 줄에는 임의의 표현식(@ 필드의 경우에 한해)이 올 수 있기 때문에, 다음에 올 예제와 같이 하여 좀 더 세밀한 출력 형태를 지정할 수 있다. 예를 들어 숫자 내부에 쉼표를 삽입하려 할 경우에는

format Ident =
@<<<<<<<<<<<<<<<
commify($n)

필드에 실제로 @ 나 ~, ^ 문자를 출력하고자 할 때는

format Ident =
I have an @ here.
“@”
.

전체 행을 가운데로 정렬하고자 할 때는 다음과 같이 하면 된다.

format Ident =
@||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
“Some text line”

필드의 길이를 지시하는 >는 해당되는 텍스트를 필드 내에서 오른쪽으로 정렬하도록 하지만, 전체적으로는 >가 나타난 곳부터 필드가 시작된다. 따라서 “이 필드의 길이가 얼마가 되든지 오른쪽으로 정렬하라”는 식으로의 지정은 불가능하다. 반드시 왼쪽 여백으로부터 상대적으로 얼마나 떨어져 있는지 구체적으로 지정해야 한다. 이런 식으로의 포맷을 지정하고자 한다면, 다음과 같이 현재의 컬럼 숫자에 근거하여 포맷 문자열을 만든 후, eval을 사용하는 방법이 있다.

$format = “format STDOUT = \n”
. ‘^’ . ‘<‘ x $cols . “\n”
. ‘$entry’ . “\n”
. “\t^” . “<” x ($cols-8) . “~~\n”
. ‘$entry’ . “\n”
. “.\n”;
print $format if $Debugging;
eval $format;
die $@ if $@;

위의 예제에서 가장 중요한 행은 print일 것이다. 실제로 print가 출력할 내용은 다음과 같다.

format STDOUT =
^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$entry
^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~~
$entry
.

다음은 fmt(1)과 비슷한 기능을 하는 프로그램의 예이다.

format =
^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ~~
$_
.
$/ = “”;
while (<>) {
    s/\s*\n\s*/ /g;
    write;
}

바닥글(Footer)

$FORMAT_TOP_NAME이 현재 포맷의 머리글을 지정하는 데 비해, 바닥글에 해당하는 것은 없다. 바닥글을 구현하는 한 가지 방법으로, 고정된 크기의 바닥글인 경우 매 쪽 마다 $FORMAT_LINES_LEFT를 write하기 전에 체크한 다음 바닥글에 해당되는 내용을 print하도록 할 수 있다.

또 다른 방법으로는, open(MESELF, “|-“)과 같이 프로그램 자신으로의 파이프를 연 다음(3 장의 open 항목을 참조), STDOUT대신 항상 MESELF쪽으로 write하도록 하는 것이다. 그리고 나서 자프로세스(child process)로 하여금 머리글과 바닥글에 대한 처리를 하도록 한다. 편리한 방법은 아니지만, 어쨌든 바닥글을 구현할 수 있다는 것을 명심해 두자.

포맷 내부로의 접근

포맷 메커니즘으로의 저수준 접근이 필요한 경우 formline이나 $^A($ACCUMULATOR 변수)를 이용하여 직접 접근을 할 수 있다. 예를 들어

$str = formline <<‘END’, 1,2,3;
@<<< @||| @>>>
END
print “Wow, I just stored `$^A’ in the accumulator!\n”;

혹은 다음과 같이 printf에 해당하는 sprintf가 있듯이 write에 해당하는 swrite() 서브루틴을 만들 수 있다.

use Carp;
sub swrite {
    croak “usage: swrite PICTURE ARGS” unless @_;
    my $format = shift;
    $^A = “”;
    formline($format,@_);
    return $^A;
}
$string = swrite(<<‘END’, 1, 2, 3);
Check me out
@<<< @||| @>>>
END

print $string;

my로 선언된 변수는 포맷문이 해당 변수의 범위 내에서 선언되지 않은 경우에는 포맷문 내부에서 사용될 수 없다.

특수 변수(Special Variable)

지금부터 설명할 변수들은 Perl에서 특별한 의미를 갖는 것들이다. 대부분은 의미와 관계 있는 기호로 이루어져 있거나 쉘에서 사용되는 것들이지만, 필요한 경우 English모듈을 사용하여 그 의미를 쉽게 알 수 있는 영문으로 된 긴 변수명을 사용할 수 있다.

use English;

위와 같이 하여 현재 패키지 내에서 짧은 변수 이름을 긴 이름으로 대신 사용할 수 있다. 이들 변수 중 일부는 읽기 전용이다. 즉, 읽기 전용 변수에 어떤 값을 대입하고자 하는 경우에는 실행시 예외처리(exception)가 수행된다.

정규 표현식 특수 변수

다음은 정규 표현식 및 패턴 일치와 관계 있는 몇몇 변수들이다. 항상 현재 블록에 대해 지역 변수로 취급되는 $*를 제외하고는, 다른 경우 해당 변수를 local로 취급할 필요가 없다.

$digit
가장 최근에 일치된 순서대로 괄호에 해당하는 일치된 문자열을 포함하고 있다.(\digit와 같은 형태로도 사용된다.) 읽기 전용이다.

$&
$MATCH
가장 최근에 성공적으로 일치된 문자열이며, 블록 내에서 감추어져 일치된 내용이나 현재 블록에서 eval로 둘러 싸여 있는 내용은 포함하지 않는다. 읽기 전용이다.

$`
$PREMATCH
가장 최근에 성공적으로 일치된 문자열의 앞부분에 해당하는 문자열이며, 블록 내에서 감추어져 일치된 내용이나 현재 블록에서 eval로 둘러 싸여 있는 내용은 포함하지 않는다. 읽기 전용이다.

$’
$POSTMATCH
가장 최근에 성공적으로 일치된 문자열의 뒷부분에 해당하는 문자열이며, 블록 내에서 감추어져 일치된 내용이나 현재 블록에서 eval로 둘러 싸여 있는 내용은 포함하지 않는다. 예를 들면
$_ = ‘abcdefghi’;
/def/;
print “$`:$&:$’\n”; # abc:def:ghi를 출력
이 변수는 읽기 전용이다.

$+
$LAST_PAREN_MATCH
가장 최근의 일치 패턴에 의해 마지막 괄호로 일치된 문자열이며, 실제로 어떤 교대 문자열이 일치되었는지 모를 경우에 유용하다. 예를 들어
/Version: (.*)|Revision: (.*)/ && ($rev = $+);
읽기 전용이다.
$*

$MULTILINE_MATCHING
$*는 현재 버전에서는 사용하지 않으며, 이전 버전의 Perl 프로그램과의 호환을 위해 남겨진 것이다. 그 대신 정규 표현식 내에서 /m이나 /s를 사용하기 바란다.
문자열 내에서 여러 줄의 일치를 할 경우 1로 지정하고, 한 줄로 처리할 경우 0으로 지정한다. $*가 0일 때 문자열 내에 여러 개의 개행문자가 존재할 경우 패턴 일치 결과가 이상한 값으로 나올 수 있다. 기본값은 0이다. 이 변수의 값은 ^와 $의 해석에만 관계된다는 것을 주목하기 바란다. 문자로서의 개행문자는 $* == 0이더라도 항상 검색 가능하다.

Perl 파일 핸들 특수 변수

파일 핸들에 관련된 특수 변수는 항상 현재 선택된 출력 파일 핸들에 관계된 값을 갖고 있기 때문에 절대 local로는 취급되지 않는다. 각 파일 핸들은 자신에 해당하는 일련의 특수 변수값을 따로 갖고 있다. 따라서 select로 다른 파일 핸들을 지정하여 특수 변수 값을 변경하더라도, 나중에 다시 select로 이전의 파일 핸들을 지정하게 되면 이전 파일 핸들에 관한 특수 변수 값이 그대로 유지되어 있다.

이러한 select문과 변수를 사용하지 않으려면, 해당하는 파일 핸들에 FileHandle 객체에 해당하는 객체 메쏘드를 호출하는 것으로 대신하면 된다.(이후로는 HANDLE로 표기한다.) 먼저 다음과 같이 파일핸들(FileHandle) 모듈을 사용한다고 컴파일러에게 지시하여야 한다.

use FileHandle;

그 다음 다음 두 가지 형식 중 한 가지를 사용하면 된다.

method HANDLE EXPR

혹은

HANDLE->method(EXPR)

각각의 메쏘드는 파일핸들 속성(FilleHandle attribute)의 이전 값을 반환한다. 메쏘드는 부가적으로 EXPR을 취하며, EXPR이 제공되는 경우 새로운 파일핸들 속성 값으로 지정된다. EXPR이 없는 경우 autoflush를 제외하고는 대부분의 메쏘드는 현재 값에 대해 아무런 작업도 하지 않는다.

$|
$OUTPUT_AUTOFLUSH
autoflush HANDLE EXPR

0이 아닌 값으로 지정하면 현재 선택된 출력 채널로 매번 writeprint가 행해질 때마다 fflush(3)을 수행하도록 한다.(이것을 “명령어 버퍼링(command buffering)”이라고 하며, 보통 생각하는 것과는 달리, 이 변수의 값을 설정하더라도 버퍼링을 해제하는 것은 아니다.) 기본값은 0이며, 이것은 많은 시스템에서 터미널로 출력하는 경우 STDOUT은 기본적으로 라인 버퍼링(line buffering)으로, 그 밖의 경우 블록 버퍼링(block buffering)으로 처리되는 것을 의미한다. 이 변수의 값을 지정하는 것은 rsh에서 Perl 스크립트를 실행하면서 그 출력 결과를 보고 싶을 때와 같은 경우 유용하다. 이 변수는 입력 버퍼링에는 아무런 영향도 주지 않는다.

$%
$FORMAT_PAGE_NUMBER
format_page_number HANDLE EXPR

현재 선택된 출력 채널의 현재 쪽 번호(%는 nroff에서 쪽 번호)

$=
$FORMAT_LINES_PER_PAGE
format_lines_per_page HANDLE EXPR

현재 선택된 출력 채널의 출력 가능한 줄의 수. 기본값은 60

$-
$FORMAT_LINES_LEFT
format_lines_left HANDLE EXPR

현재 선택된 출력 채널의 쪽에서 남아 있는 줄 수

$~
$FORMAT_NAME
format_name HANDLE EXPR

현재 선택된 출력 채널에 적용될 포맷의 이름. 기본값은 파일 핸들의 이름

$^
$FORMAT_TOP_NAME
format_top_name HANDLE EXPR

현재 선택된 출력 채널에 적용될 머리글 포맷의 이름. 기본값은 파일 핸들에 _TOP을 붙인 것

전역 특수 변수(Global Special Variable)

다음 변수들은 전역 특수 변수로서 모든 패키지 내에서 동일하게 취급되는 것이다. 만약 이들 변수의 복사값을 사용하고자 할 경우, 현재 블록에서 지역화하여 사용하여야 한다.

$_
$ARG

기본 입력 및 패턴 일치 공간. 다음 각 쌍은 동일한 효과를 갖는다.

while (<>) {…} # only equivalent in while!
while (defined($_ = <>)) {…}

/^Subject:/
$_ =~ /^Subject:/

tr/a-z/A-Z/
$_ =~ tr/a-z/A-Z/

chop
chop($_)

다음과 같은 경우 $_를 사용하지 않았더라도 Perl은 $_를 사용하는 것으로 간주한다.

­· ord, int 등과 같은 다양한 일원 함수 및 -t를 제외한 모든 파일 테스트 연산자(-f, -d 등). -t는 기본으로 STDIN을 취한다.
· printunlink등과 같은 다양한 리스트 함수
· =~ 연산자가 없는 경우의 패턴 일치 연산 m//, s///, tr///
· 다른 특별한 변수가 없는 경우 foreach 루프의 기본 반복 변수
· grepmap 함수의 묵시적 기본 반복 변수
· while 문의 조건 테스트에서 <FH> 연산의 결과를 테스트할 때 입력 레코드가 저장되는 기본 장소. while 문의 밖에서는 아무 일도 일어나지 않는데 주의한다.

$.
$INPUT_LINE_NUMBER
$NR
가장 최근에 읽은 파일 핸들의 현재 입력 행 번호. 명시적으로 파일 핸들을 닫으면 이 값을 리셋하는 효과가 있다. < >는 명시적으로 파일 핸들을 닫지 않으므로, ARGV로 지정된 파일에 대해서는 행 번호는 계속 증가한다.(3 장의 eof 항목의 예제를 참조) $.를 지역화하는 것은 Perl이 가장 최근에 읽어 들인 파일 핸들을 지역화 하는 효과가 있다.

$/
$INPUT_RECORD_SEPARATOR
$RS
입력 레코드 구분자로 기본값은 개행문자이다. awk의 RS변수와 비슷하게 동작하며, 만약 널 문자열로 지정되면 빈 줄(blank line)을 구분자로 취급한다. 여러 개의 글자를 구분자로 사용하기 위해서 여러 개의 글자로 이루어진 문자열을 지정할 수도 있다. 파일 내에 연속해서 공백 행이 있을 경우 이 값을 “”로 지정하는 것과 “\n\n”로 하는 것과 차이가 있음을 주목하기 바란다. “”로 하는 경우는 둘 이상의 연속된 공백 행을 하나의 공백 행으로 처리한다. “\n\n”으로 지정하면 Perl은 다음 번 입력 문자는 비록 개행 문자가 오더라도 다음 번 단락에 해당되는 글자로 간주한다.

undef $/;
$_ = <FH>; # whole file now here
s/\en[ \et]+/ /g;
$,
$OUTPUT_FIELD_SEPARATOR
$OFS

print 연산자에 적용되는 출력 필드 구분자이다. 보통 print 연산자는 쉼표로 구분된 필드를 출력한다. awk에서와 비슷하게 동작하도록 하려면, awk의 OFS 변수를 지정해서 출력될 필드를 구분하는 것과 마찬가지 방법으로 이 변수를 지정하면 된다.

$\
$OUTPUT_RECORD_SEPARATOR
$ORS
print 연산자에 적용되는 출력 레코드 구분자이다. 보통 print 연산자는 쉼표로 구분된 필드를 출력하며, 뒤에는 개행문자나 레코드 구분자가 없다고 가정한다. awk에서와 비슷하게 동작하도록 하려면, awk의 ORS변수를 지정해서 print의 마지막 부분에 출력될 내용을 지정하는 것과 같이 이 변수를 지정하면 된다.

$*
$LIST_SEPARATOR
이 변수는 겹 따옴표 문자열에서 치환되는 리스트 값에 적용된다는 점만 제외하고는 $,와 동일하다. 기본값은 스페이스이다.

$;
$SUBSCRIPT_SEPARATOR
$SUBSEP
다차원 배열 연산에서의 첨자 구분자(subscript separator)이다. 만약 다음과 같이 해쉬 요소를 참조하는 경우

$foo{$a,$b,$c}

실제로는 다음을 의미한다.

$foo{join($;, $a, $b, $c)}

그러나 다음과 같이 할 경우

@foo{$a,$b,$c} # 슬라이스 – @에 주의

실제로는 다음을 의미하게 되므로 주의하여야 한다.

($foo{$a},$foo{$b},$foo{$c})

기본값은 34로서 awk에서의 SUBSEP과 같은 값이다. 만약 키 값에 이진 데이터가 포함되어 있을 경우 $;값이 적절하지 않을 수도 있다.

이 변수는 이전 버전의 Perl 프로그램과의 호환을 위해 남겨진 것으로서, 현재 버전에서는 실제 제공되는 다차원 배열을 사용하는 것을 권한다.

$^L
$FORMAT_FORMFEED
format_formfeed HANDLE EXPR
쪽 넘김(formfeed)시 출력될 내용을 지정한다. 기본값은 “\f”이다.

$:
$FORMAT_LINE_BREAK_CHARACTERS
format_line_break_characters HANDLE EXPR
문자열이 나뉘어 질 때 포맷문에서 ^로 시작하는 연속 필드를 채우는데 사용되는 문자의 세트를 지정한다. 기본값은 ” \n-“이다.

$^A
$ACCUMULATOR
format 행에 대한 write accumulator의 현재값을 나타낸다. formline을 포함하는 포맷문을 사용하여 결과를 $^A에 넣도록 할 수 있다. 포맷을 알아낸 다음 write$^A의 내용을 출력한 다음 $^A를 비운다. 따라서 formline을 사용하지 않고서는 $^A의 값을 알아낼 수 없다.

$#
$OFMT
$#는 현재는 사용되지 않으며 이전 버전의 프로그램과의 호환을 위해 존재한다. 대신 printf를 사용하기 바란다. $#는 출력될 숫자의 출력 포맷을 포함한다. awk의 OFMT 변수와는 다르게 동작하는 면이 있다. 초기값도 awk의 경우 %.6g인데 비해 Perl에서는 %.14g이다.

$?
$CHILD_ERROR
가장 최근에 닫은 파이프, 백틱(`)명령, 혹은 system 연산자 등에서 반환된 결과를 담고 있다. 실제로 이 값은 wait(2) 시스템 호출에 의해 반환된 상태이므로, 서브프로세스의 종료값은 실제로 ($? >> 8)에 해당한다. 따라서 많은 시스템에서 $? & 255하면 실제로 프로세스로부터 어떤 시그널이 발생했는지, 코어 덤프가 발생했는지의 여부를 알 수 있다.

$!
$OS_ERROR
$ERRNO
숫자 구문에서 사용될 경우 현재 실행 중인 Perl에서 errno 변수(가장 최근의 시스템 호출 오류)의 현재 값을 제공한다. 문자열 구문에서 사용될 경우, 해당되는 시스템 오류 문자열을 제공한다. 오류 n에 대해 $!가 특정 문자열을 반환하도록 하기 위해서 errno를 지정하거나, die 연산자의 종료 값을 지정하기 위해서 $! 변수에 특정 값을 대입할 수도 있다.

$@
$EVAL_ERROR
가장 최근의 eval 명령어를 수행한 결과 발생한 문법 오류 메시지를 담고 있다. 최근 eval 명령어가 정상적으로 수행되어 오류가 발생하지 않았다면 널 값을 갖는다. 경고 메시지는 이 변수에 포함되지 않음을 주의하기 바란다. 필요한 경우 $SIG{__WARN__}을 지정하여 경고 메시지를 처리하는 루틴을 만들 수도 있다.

$$
$PROCESS_ID
$PID
perl이 현재 스크립트를 수행하는 프로세스 번호

$<
$REAL_USER_ID
$UID
현재 프로세스의 실 user ID(uid).

$>
$EFFECTIVE_USER_ID
$EUID
현재 프로세스의 유효 uid. 예를 들면
$< = $>; # 실 uid를 유효 uid로 설정
($<,$>) = ($>,$<); # 실 uid와 유효 uid를 스왑
$<와 $>는 setreuid(2)를 지원하는 시스템에서만 교환할 수 있다.

$(
$REAL_GROUP_ID
$GID
현재 프로세스의 실 GID. 현재 시스템이 동시에 여러 그룹으로의 멤버쉽을 지원하는 경우 스페이스로 구분된 그룹의 리스트를 갖고 있다. 이 경우 첫 번째 숫자는 getgid(1)의 반환값이고, 나머지는 getgroups(2)의 반환값으로서 이 중 하나는 첫 번째 숫자와 같은 값을 갖는다.

$)
$EFFECTIVE_GROUP_ID
$EGID
현재 프로세스의 유효 GID. 현재 시스템이 동시에 여러 그룹으로의 멤버쉽을 지원하는 경우 스페이스로 구분된 그룹의 리스트를 갖고 있다. 이 경우 첫 번째 숫자는 getegid(1)의 반환값이고, 나머지는 getgroups(2)의 반환값으로서 이 중 하나는 첫 번째 숫자와 같은 값을 갖는다.
$<, $>, $(, $)는 각각에 해당되는 set-id 루틴을 지원할 때에만 지정할 수 있다. $($)setegid(2)를 지원하는 시스템에서만 교환할 수 있다. 현재 Perl에서는 initgroups(2)를 사용하지 않으므로, 여러 개의 그룹으로 그룹 벡터(group vector)를 지정할 수 없다.

$0
$PROGRAM_NAME
현재 수행 중인 Perl 스크립트를 포함하고 있는 파일의 이름. $0에 값을 대입하여 ps(1) 프로그램이 참조하는 영역을 변경할 수 있다. 이것은 현재 수행 중인 프로그램을 숨기는 것보다 현재 프로그램의 상태를 나타내는 방법으로 유용하게 쓰인다. 그러나 모든 시스템에서 이처럼 동작하지는 않는다.

$[
배열의 첫 번째 요소의 인덱스, 서브 문자열 내의 첫 번째 글자를 의미함. 기본값은 0이지만, index를 평가하거나 substr 함수를 사용하는 경우 Perl을 awk 나 FORTRAN 등과 비슷하게 동작하도록 하기 위해서는 1로 지정할 수 있다.

$]
$PERL_VERSION
현재 Perl의 버전 + 패치 레벨/1000을 반환한다. 스크립트 앞 부분에서 해당 스크립트를 실행하는 Perl 인터프리터가 정상 버전인지를 알아내는 데 사용한다. 예를 들면,
warn “No checksumming!\n” if $] < 3.019;
die “Must have prototyping available\n” if $] < 5.003;

$^D
$DEBUGGING
디버깅 플랙(debugging flag)의 현재 값

$^F
$SYSTEM_FD_MAX
시스템 파일 기술자(system file descriptor)의 최대값으로, 기본적으로 2이다. 시스템 파일 기술자는 실제로 실행되는 프로세스로 전달되는 반면, 그 외의 파일 기술자는 전달되지 않는다. 또한, open 과정에서 비록 open이 실패하더라도 시스템 파일 기술자는 보존된다.(다른 파일 기술자는 open이 시도되기 전에 close되며, open이 실패하면 close 된 상태로 남아 있게 된다.) 파일 기술자의 close-on-exec 상태는 open$^F의 값에 따라 결정되며 exec시에 결정되는 것이 아니다.

$^H
특정 pragmatic 모듈에 의해 활성화 되는 내부 컴파일러 힌트값.(이 값은 무시하고 pragma 문을 사용하라.)

$^I
$INPLACE_EDIT
inplace_edit 확장의 현재 값. inplace 편집을 비활성화하려면 undef를 사용하면 된다.

$^O
$OSNAME
현재 Perl 바이너리가 컴파일된 시스템의 운영체제 이름. Config 모듈 대신 사용할 수 있는 간단한 방법이다.

$^P
$PERLDB
디버거가 자기 자신을 디버그하지 않도록 클리어하는 내부 플래그. 사용자가 이 값을 클리어하여 확실하게 디버그를 하지 않도록 할 수 있다.

$^T
$BASETIME
1970년부터 시작하여 해당 스크립트가 실행된 시간을 초 단위로 환산한 값. -M, -A, -C와 같은 파일 테스트 연산자는 이 값에 근거한다.

$^W
$WARNING
현재 경고 스위치(참 또는 거짓) 의 값

$^X
$EXECUTABLE_NAME
실행된 Perl 바이너리 자신의 이름. C에서의 argv[0]에 해당.

$ARGV
<ARGV>로부터 읽어 들일 때 현재 파일의 이름

전역 특수 배열(Global Special Array)

다음 배열과 해쉬는 전역 배열, 전역 해쉬이다. 특수 전역 스칼라 변수와 마찬가지로, 해당되는 값이 참조될 때는 main 패키지를 참조한다. 다음 두 문장은 정확히 같은 문장이다.

print “@INC\n”;
print “@main::INC\n”;
@ARGV

해당 스크립트에 대한 명령어 행 파라미터를 담고 있는 배열. $#ARGV는(파라미터의 수 1)을 담고 있고, $ARGV[0]는 프로그램(스크립트) 이름이 아니라 전달된 첫 번째 파라미터를 담고 있다는 것을 주의하기 바란다. 프로그램 이름은 $0을 참조하라.

@INC

Perl 스크립트가 do EXPR, require, use 구조를 사용할 때 참조해야 하는 위치 리스트를 담은 배열. 초기에는 -I 명령어 행 스위치에 해당하는 값과, 다음과 같은 기본 Perl 라이브러리의 위치를 포함하고 있다.

/usr/local/lib/perl5/$ARCH/$VERSION
/usr/local/lib/perl5
/usr/local/lib/perl5/site_perl
/usr/local/lib/perl5/site_perl/$ARCH

현재 디렉토리를 참조하려면 “.”를 추가하면 된다. 실행시 @INC 리스트를 변경할 필요가 있을 때는, 시스템과 관련된 라이브러리 모듈을 제대로 불러 들이기 위해 다음과 같이 lib 모듈을 사용해야만 한다.

use lib ‘/mypath/libdir/’;
use SomeMod;

@F

-a 스위치가 사용되었을 때 입력 행이 split되어 입력되는 배열. -a 스위치가 사용되지 않으면 이 배열은 특별한 의미가 없다.

%INC

dorequire를 통해 포함된 각 파일의 파일명을 담고 있는 해쉬. 키는 사용자가 지정하는 파일명이고, 값은 실제로 해당 파일을 찾은 위치를 갖고 있다. require 명령을 사용할 때, 이 값을 참조하여 주어진 파일이 이미 포함되었는지의 여부를 결정한다.

%ENV

현재 환경 변수의 값을 담고 있다. 다음과 같이 %ENV의 값을 변경하면 자식 프로세스의 환경을 바꾸게 된다.

$ENV{PATH} = “/bin:/usr/bin”;

환경 변수에서 특정한 값을 없애려면 undef 대신 delete를 사용해야 한다.

crontab의 요소로 실행되는 프로세스의 경우 환경 변수의 특정 세트만 물려 받게 됨을 주의하기 바란다. 또한, setuid 스크립트로 실행할 경우 $ENV{PATH}, $ENV{SHELL}, $ENV{IFS}를 반드시 지정해야 한다. 보안이나 setuid에 관련해서는 8장을 참조하기 바란다.

%SIG

다양한 시그널을 처리하기 위한 시그널 핸들러에 의해 사용되는 해쉬. 예를 들면

sub handler { # 1st argument is signal name
    local($sig) = @_;
    print “Caught a SIG$sig–shutting down\n”;
    close(LOG);
    exit(0);
}
$SIG{INT} = ‘handler’;
$SIG{QUIT} = ‘handler’;
$SIG{INT} = ‘DEFAULT’; # restore default action
$SIG{QUIT} = ‘IGNORE’; # ignore SIGQUIT

%SIG 배열은 Perl 스크립트 내에서 실제로 지정된 시그날에 대한 값만 포함한다. 여기 몇 가지 예를 들면

$SIG{PIPE} = Plumber; # SCARY!!
$SIG{PIPE} = “Plumber”; # just fine, assumes main::Plumber
$SIG{PIPE} = \&Plumber; # just fine; assume current Plumber
$SIG{PIPE} = Plumber(); # oops, what did Plumber() return??

위에서 SCARY!!로 표시된 예제는 bareword를 사용하였기 때문에 문제의 소지가 있다. 즉, bareword는 때로는 함수를 나타내는 문자열로 표시될 수 있고 때로는 서브루틴을 호출하는 데에도 사용되기 때문이다. 따라서 그 다음의 예제와 같이 인용 부호를 사용하던가 레퍼런스를 사용해야 한다. 몇몇 내부 후크(hook) 또한 %SIG 해쉬를 이용해 지정할 수 있다. 경고 메시지가 출력될 때 $SIG{__WARN__}로 지정된 루틴이 호출된다. 이 때 경고 메시지는 첫 번째 파라미터로 전달된다. __WARN__ 후크가 존재하면 일반적으로 STDERR로 출력되던 경고 메시지가 출력되지 않게 된다. 이런 방법을 이용하여 경고 메시지를 특정 변수에 저장하거나, 다음과 같이 경고 대신 치명적 오류로 전환할 수 있다.

local $SIG{_\|_WARN_\|_} = sub { die $_[0] };
eval $proggie;

$SIG{__DIE__}로 지정된 루틴은 치명적 예외 처리가 수행될 때 호출된다. 오류 메시지는 첫 번째 파라미터로 전달된다. __DIE__ 후크 루틴에서 반환될 때, 후크 루틴 자신이 goto나 루프 종료, die등에 의해 종료되지 않는 경우 예외 처리 루틴은 마치 해당 후크가 없었던 것처럼 동작한다. __DIE__ 핸들러는 호출된 동안 명시적으로 비활성되어, __DIE__ 핸들러에서 실제 die를 호출할 수 있게 된다.(만약 이렇게 하지 않으면, 핸들러 자신이 무한정 재귀 호출되게 된다.) 이것은 __WARN__의 경우와 비슷하다.

전역 특수 파일 핸들(Global Special Filehandle)

다음은 DATA를 제외하고는 항상 main::FILEHANDLE을 참조한다.

ARGV
@ARGV에 포함된 명령어 행 파일명에 대해 반복적으로 수행되는 특수한 파일 핸들. 보통 널 파일 핸들 < >로 표시된다.

STDERR
임의의 패키지에서 표준 오류를 위한 특수 파일 핸들

STDIN
임의의 패키지에서 표준 입력을 위한 특수 파일 핸들

STDOUT
임의의 패키지에서 표준 출력을 위한 특수 파일 핸들

DATA
스크립트를 포함하는 파일에서 __END__ 토큰 다음에 나타나는 임의의 내용을 참조하는 특수 파일 핸들. 혹은, __DATA__를 찾은 같은 패키지에서 데이터를 읽는 동안 필요로 하는 파일 내에 __DATA__ 토큰 다음에 나타나는 임의의 내용을 위한 파일 핸들

_ (밑줄)

가장 최근의 stat, lstat, 파일 테스트 연산 결과에 관한 정보를 캐쉬하기 위해 사용되는 특수 파일 핸들

 

 

함수

이 장에서는 Perl 함수에 대해 설명한다. 이들 함수는 알파벳 순서대로 하나씩 설명한다.(그 중 몇몇 함수는 둘, 셋 혹은 네개의 함수를 한꺼번에 설명한다. 이것은 대개 Perl 함수가 유닉스 시스템 호출이나 C 라이브러리를 호출한 경우이다. 그럴 경우 Perl 함수는 상응하는 유닉스 매뉴얼 페이지(manpage) 구성 형태와 유사한 형태로 표기된다..

각 함수에 대한 설명은 먼저 간단한 문법(syntax) 소개로 시작한다. 대문자만으로 표기된 인자는 함수 설명 부분처럼 실제 식의 위치를 나타낸다. 특정 인자는 선택사양이다: 텍스트는 그 인자가 포함되지 않은 경우에 사용되는 디폴트 값을 설명해 준다.

이 장에서 설명되는 함수들은 문자나 변수와 함께 식의 한 용어로 사용될 수 있다.(또는 사용자는 함수를 일종의 접두(prefix) 연산자로 이해해도 좋다. 여기서는 당분간 연산자로 취급한다.) 이들 연산자 즉 함수들중 몇 몇은 LIST를 인자로 사용한다. 그러한 리스트는 스칼라와 리스트 값들의 조합으로 구성될 수 있다. 그러나, 모든 리스트 값은 일련의 스칼라 값으로 치환(interpolation)된다. 다시 말해 전체 인자 LIST는 1차원 리스트 값인 것이다.(배열을 하나의 요소로 해석하기 위해서는 명시적으로 배열의 레퍼런스를 생성 및 치환(interpolation)해야 한다.) LIST 요소들은 콤마로 분리되어야 한다.(혹은 콤마 대신에 재미있는 표기의 하나인 =>를 사용기도 한다.) LIST의 각 요소는 리스트 구문에서 평가되어야 한다.

이 장에서 설명되는 함수는 인자를 표기시 괄호를 사용하기도 하고 때로는 사용하지 않기도 한다.(문법 설명은 괄호를 사용하지 않는다.) 만약 사용자가 괄호를 사용하고자 한다면 다음과 같은 단순한 규칙(때로는 놀라울 정도로 도움이 되는)을 사용하면 된다: 만약 함수처럼 보이면 그것은 함수이다. 그리고 순서는 문제가 되지 않는다. 만약 그렇지 않으면 그것은 리스트 연산자 혹은 단일 연산자이며 순서가 중요한 의미를 가진다. 그리고 함수와 좌괄호 사이의 공백은 중요하지 않다. 따라서, 사용자는 때로는 주의해야 한다.

print 1+2+3; # 6을 출력

print(1+2) + 3; # 6을 출력

print (1+2)+3; # 3을 출력!

print +(1+2)+3; # 6을 출력

print ((1+2)+3); # 6을 출력

만약 -w 스위치를 사용하여 Perl을 구동시키면 사용자는 어떤 메시지를 가진다. 예를 들어 앞의 셋째 열의 예제를 실행시키면 다음과 같은 메시지를 볼 수 있다:

print (…) interpreted as function at – line 3.

Useless use of integer addition in void context at – line 3.

몇몇 LIST 연산자는 리스트의 첫번째 혹은 두번째 요소에 특별한 시맨틱(semantic) 중요성을 부여한다. 예를 들면 chmod 함수는 리스트의 첫번째 요소는 나머지 요소로 나타낸 새로운 파일 퍼미션(permission)을 설정하게 된다. 그러나, 문법적으로 chmod의 인자는 단지 LIST이며 사용자는 다음과 같이 사용할 수 있다. 즉 chmod 0644, @array는

unshift @array, 0644;

chmod @array;

다음과 같다.

chmod 0644, @array;

이러한 경우 섹션의 맨 위 문법 요약 부분은 단지 LIST를 의미하며, 어떤 특수한 초기 인자는 설명란에서 설명된다.

한편 만약 문법 요약이 LIST 앞부분에 어떤 인자를 가지면, 그 인자들은 문법적으로 구별되며(문법적으로만 구별되는 것은 아니고), 사용자가 함수를 호출시에 전달하는 실제 인자들에 대한 문접적 제약을 가한다. 예를 들면, push 함수의 첫번째 인자는 반드시 배열명이어야 한다.(사용자는 또한 프로토타입 사용시 서브루틴 선언에서 문법적인 제약을 명시해야 하기도 한다. 2장의 Perl 자세히 들여다보기의 프로토타입을 참고하라 )

이러한 많은 연산들은 C 라이브러리 함수를 이용해서 수행된다. 그 함수에 대한 설명은 UNIX 시스템 문서와 같기 때문에 여기서 설명하지 않는다, 따라서 사용자는 직접 유닉스의 매뉴얼 페이지를 참조하기 바란다. 다음이 바로 그러한 경우이다. “getlogin(3)을 참고하라” 괄호 안의 숫자는 주어진 항목에 대한 유닉스 매뉴얼의 섹션 번호를 나타낸다.(유닉스에서는 동일한 이름의 함수들이 존재하므로 함수들 성격에 따라 섹션을 분리하여 매뉴얼을 구성하였다.) 만약 사용하는 시스템에서 특정한 C 함수에 대한 매뉴얼 페이지를 찾지 못하면, 그에 대응되는 Perl 함수 역시 구현되지 않았을 가능성이 높다. 예를 들면 소켓 호출이 모든 시스템에 구현되어 있는 것은 아니다. MS-DOS의 경우 소켓 호출은 있으나 fork와 같은 것은 없다.(잘 생각해 보면 해당하는 매뉴얼 페이지도 없다는 것을 알 수 있다.) 때로는 설명되어 있는 C 함수가 상응하는 Perl 함수보다 더 많은 인자를 가지는 경우도 있다. 그럴 경우 부족한 인자는 대개 Perl이 이미 알고 있는 인자여서 별도로 명시하지 않아도 되는 것이다. 이는 Perl과 C가 파일핸들 그리고 그 성공/실패 값을 표기하는 방법이 다르기 때문이다.

스칼라 혹은 리스트에 사용되는 함수의 경우, 중단되지 않는 실패(non-abortive failure)는 정의 되지 않은 값을 돌려 주어 스칼라 구문 형태로, 그리고 널 리스트를 돌려 주어 리스트 구문 형태로 나타난다. 실행값이 성공적이면 문에서 그 결과값은 대개 참값으로 나타난다.

‘리스트를 스칼라로 변환하는 일반적인 규칙은 없다‘라는 것을 기억하기 바란다.

많은 연산자들은 리스트 구문에 리스트를 반환한다. 각 연산자는 스칼라와 리스트 구문에서 어떤 형태로 사용되는 지를 알고 있다. 그리고, 스칼라 구문에서는 가장 적합한 종류의 값을 반환한다. 어떤 연산자는 리스트 구문에서 사용된 경우 리스트의 길이를 값으로 돌려 준다. 어떤 연산자는 리스트의 첫번째 값을 돌려 주기도 하고 혹은 맨 마지막 값을 돌려 주기도 한다. 어떤 연산자는 숫자나 이름을 검색하여 첫번째 값이나 마지막 값이 아닌 다른 값을 돌려 준다. 어떤 연산자는 성공적으로 실행된 연산의 개수를 반환한다. 만약 사용자가 일관성을 꼭 고집하지 않는다면, Perl 연산자는 대개 사용자가 원하는 형태의 결과를 준다.

범주에 의한 Perl 함수 분류

다음은 범주에 의해 분류된 Perl 함수와 함수 같은 키워드들이다. 어떤 함수는 두개 이상의 표제에 속한다. .

스칼라 처리

chomp, chop, chr, crypt, hex, index,lc, lcfirst, length, oct, ord, pack, q//,qq//,

reverse, rindex, sprintf, substr, tr///,uc, ucfirst, y///

정규식과 패턴 일치

m//, pos, quotemeta, s///, split, study

수치 함수

abs, atan2, cos, exp, hex, int, log, oct, rand, sin, sqrt, srand

배열 처리

pop, push, shift, splice, unshift

리스트 처리

grep, join, map, qw//, reverse, sort, unpack

해쉬 처리

delete, each, exists, keys, values

입출력 처리

binmode, close, closedir, dbmclose, dbmopen,die, eof, fileno, flock, format, getc, print, printf, read, readdir, rewinddir, seek, seekdir, select (ready file descriptors), syscall, sysread, syswrite, tell, telldir, truncate, warn, write

고정길이 데이터 및 레코드

pack, read, syscall, sysread, syswrite, unpack, vec

파일핸들과 파일 그리고 디렉토리 관련 함수

chdir, chmod, chown, chroot, fcntl, glob, ioctl, link, lstat, mkdir, open,opendir, readlink, rename, rmdir, stat, symlink, sysopen, umask, unlink, utime

함수 흐름 제어관련 함수

caller, continue, die, do, dump, eval, exit, goto, last, next, redo, return, sub, wantarray

영역

caller, import, local, my, package, use

특수

defined, dump, eval, formline, local, my, reset, scalar, undef, wantarray

프로세스와 프로세스 그룹

alarm, exec, fork, getpgrp, getppid, getpriority, kill, pipe, qx//, setpgrp, setpriority, sleep, system, times, wait, waitpid

라이브러리 모듈

do, import, no, package, require, use Classes and objects bless, dbmclose, dbmopen, package, ref, tie, tied, untie, use

로우레벨 소켓 접근

accept, bind, connect, getpeername, getsockname, getsockopt, listen, recv, send, setsockopt, shutdown, socket, socketpair

시스템 V 상호 통신 관련 함수

msgctl, msgget, msgrcv, msgsnd, semctl, semget, semop, shmctl, shmget, shmread, shmwrite

사용자 및 그룹 정보 수정 관련 함수

endgrent, endhostent, endnetent, endpwent, getgrent, getgrgid, getgrnam, getlogin, getpwent, getpwnam, getpwuid, setgrent, setpwent

네트웍 정보 수정 관련 함수

endprotoent, endservent, gethostbyaddr, gethostbyname, gethostent, getnetbyaddr, etnetbyname, getnetent, getprotobyname, getprotobynumber, getprotoent, getservbyname, getservbyport, getservent, sethostent, setnetent, setprotoent, setservent

시간

gmtime, localtime, time, times

알파벳 순서로 정리된 Perl 함수

/PATTERN/

/PATTERN/

m/PATTERN/

일치 연산자는 2장의 “정규 표현식”을 참조하시오

?PATTERN?

?PATTERN?

이 것은 /PATTERN// 검색과 같으며, reset을 호출하는 경우 일치하는 것을 단지 한번 찾는다. 따라서, 동일한 패턴의 것이 여러 개 존재하는 경우 일치하는 첫번째 것만을 찾는다. (다시 말하면, 연산자는 일치하는 것을 찾기위해 반복하여 검색하며, 만약 일치하는 것을 찾게되면, 사용자가 reset을 다시 호출하지 않는한 거기서 검색을 멈춘다.) 여러 개의 파일에서 어떤 특정한 패턴의 첫번째 것을 찾고자 하는 경우 연산자의 이러한 기능은 매우 유용(그리고 효율적)하다. m??는 ??와 동일한 기능을 한다는 것을 유의하라.

연산자 reset은 단지 같은 패키지내에서 컴파일된 ??의 인스턴스(instance)를 리셋한다.

abs
abs VALUE
이 함수는 인자(혹은 인자를 생략하면 $_를 인자로 취급한다)의 절대값을 반환한다.

accept
accept NEWSOCKET, GENERICSOCKET
이 함수는 accept 시스템 호출과 같은 기능을 수행한다 — accept(2)를 참고하라. 클라이언트의 소켓 커넥션 요청을 받아 들이고자 하는 서버의 프로세서에 사용된다. 커넥션이 이루어질 때까지 실행은 잠시 중지되면, 연결되면 NEWSOCKET 파일핸들이 오픈되어 새로 생성된 커넥션과 연결된다. 함수에 대한 호출이 성공적으로 수행되면 연결되는 주소를 반환하며, 실패인 경우 거짓값을 반환한다(그리고 오류코드를 $!에 저장한다). GENERICSOCKETsocket 연산자를 통해 이미 열려있는 파일핸들이어야 하며, 서버의 네트웍 주소중 하나와 바인딩(binding)되어야 한다. 예를 들어,
unless ($peer = accept NS, S) {
    die “Can’t accept a connection: $!\n”;
}
6장 사회 공학의 “소켓(Sockets)” 부분의 예제를 참고하라.

alarm
alarm EXPR
이 함수는 EXPR 초가 지난 후에 Perl 프로그램에게 SIGALRM 신호를 보낸다. 알람이 초단위로 동작하는 구형 시스템도 있다.(즉 1초보다 작은 시간에 대해서는 의미를 가지지 못한다). 예를 들어 alarm 1은 현재 시각에 따라, 지금부터 0과 1초 사이에 동작을 하게된다. 그리고 alarm 2는 지금부터 1과 2초사이에 동작을 한다. 더 나은 방법은 특정 유닉스 시스템에서 제공하는 itimer 루틴을 호출하는 syscall을 사용하는 것이다. 혹은 select 함수의 타임아웃(timeout) 기능을 사용하는 것이다.
호출할 때마다 그 전 타이머는 정지되며, 만약 0을 인자로 사용하는 경우에는 새로 타이머를 시작하지 않고 그 전의 타이머 동작을 취소한다. 결과값은 그 전 타이머의 잔여시간을 초단위로 세어 반환한다.

atan2
atan2 Y, X
이 함수는 -π에서 π까지의 범위에 속하는 Y/X의 아크탄젠트(arctangent) 값을 반환한다. π의 개략적인 값을 계산하는 빠른 방법은 다음과 같다:
$pi = atan2(1,1) * 4;
탄젠트(tagent) 연산을 하는 경우 POSIX::tan() 함수를 사용하거나 가까운 연관 관계를 사용한다:
sub tan { sin($_[0]) / cos($_[0]) }

bind
bind SOCKET, NAME
이 함수는 bind 시스템 호출과 같은 기능을 한다 — bind(2)를 참고하라. 이 함수는 파일핸들 SOCKET에 의해 지정된 이미 열린 소켓에 주소(이름)을 붙인다. 그 결과가 성공적이면 결과값은 참값이며, 그렇지 않으면 거짓값(그리고 오류코드가 $!에 저장된다)이다.
NAME은 소켓의 적절한 형태인 압축된(packed) 주소이다.
bind S, $sockaddr or die “Can’t bind address: $!\n”;
6장의 “소켓” 부분 예제를 참조하시오

binmode
binmode FILEHANDLE
크게 두가지 형태(이진 파일과 텍스트 파일)의 파일을 지원하는 OS상에서, 이 함수는 처리하려는 파일을 이진파일로 설정한다. 이 함수는 반드시 파일핸들을 열고 나서 입출력 작업이 이루어지기 전에 사용되어야 한다. 파일핸들을 이진 모드로 리셋하는 유일한 방법은 파일을 다시 여는 것이다.
이진 모드와 텍스트 모드를 구분하여 지원하는 시스텡에서는 텍스트 모드 입력시 \r\n은 \n으로, 출력시에는 \n이 \r\n으로 변환된다. UNIX나 Plan9 시스템에서는 binmode가 위와 같은 영향을 끼치지 않는다. 파일핸들이 하나의 식이면, 그 식의 값은 파일핸들의 이름으로 사용된다. 다음 예는 Perl 스크립터 파일이 제어코드가 삽입된 문서작성 파일을 어떻게 처리하는지를 보여준다.
open WP, “$file.wp” or die “Can’t open $file.wp: $!\n”;
binmode WP;
while (read WP, $buf, 1024) {…}

bless
bless REF, CLASSNAME
bless REF
이 함수는 레퍼런스 REF가 참조하는 항목을 찾아서 그 항목이 CLASSNAME 패키지내에 포함된 객체인지의 여부를 알려준다. 흔히 있는 경우이지만, 만약 CLASSNAME 패키지가 생략되면 현재의 패키지를 사용한다. bless는 종종 구성자(constructor) 함수의 맨 마지막 문이기 때문에, 이 함수는 편의상 레퍼런스를 반환한다. (만약 bless 연산을 행하는 구성자(constructor)가 파생된 클래스에서 상속되면(inherited) , 항상 2개의 인자를 사용하라. 그런 경우 사용자가 원하는 객체를 bless하려는 클래스가 일반적으로 문제의 구성자(constructor)의 첫번째 인자이다.)
객체의 bless 연산(과 blessings)에 관한 내용을 좀 더 알고자 하면 5장의 패키지, 모듈 그리고 객체 클래스편의 “객체”를 참고하라.

caller
caller EXPR
caller
이 함수는 현재의 서브루틴 호출의 스택(stack) 정보를 반환한다. 인자를 사용하지 않는 경우에는 반환값으로 현재 실행되고 있는 서브루틴을 호출한 패키지 이름, 파일이름 그리고 프로그램의 행 위치를 준다:
($package, $filename, $line) = caller;
인자를 사용하는 경우 이 함수는 EXPR을 현재 스택 프레임 수 바로 이전으로 돌아가기 위한 스택 프레임의 수로 해석한다. 그리고, 그 밖의 다른 정보도 준다.
$i = 0;
while (($pack, $file, $line, $subname, $hasargs, $wantarray) = caller($i++)) {
    …
}
게다가 DB 패키지내에서 호출된 경우에는 더 상세한 정보를 반환한다. 이 함수는 리스트 변수 @DB::args를 주어진 스택 프레임내에서 전달된 인자값으로 설정한다.

chdir
chdir EXPR
이 함수는 현재의 작업 디렉토리를 가능하다면 EXPR로 변경한다. 만약 EXPR을 사용하지 않으면 홈 디렉토리로 변경한다. 이 함수는 성공시에는 1을 그렇지 않은 경우에는 9을 반환한다.(그리고 오류 코드를 $!에 넣는다.)
chdir “$prefix/lib” or die “Can’t cd to $prefix/lib: $!\n”;
다음의 코드를 사용하면 어떻게 해서든지 사용자의 홈 디렉토리로 이동할 수 있다.
$ok = chdir($ENV{“HOME”} || $ENV{“LOGDIR”} || (getpwuid($<))[7]);
다른 방법으로는 디폴트 기능을 이용하여, 사용자는 다음과 같이 할 수 있다.
$ok = chdir() || chdir((getpwuid($<))[7]);
7장에 설명되는 Cwd 모듈도 참고하라. Perl 표준 라이브러리를 사용하여 사용자가 현재의 디렉토리를 추적할 수 있다.

chmod
chmod LIST
이 함수는 일련의 리스트의 파일 퍼미션을 변경한다. 리스트의 첫번째 요소는 chmod(2)처럼 수치 모드이어햐 한다.(비문자(nonliteral) 모드 데이터를 사용할 때, 사용자는 oct 함수를 사용하여 8진 문자열(strings)을 10진수로 변환해야 한다. 이 함수는 성공적으로 연산이 행해진 파일의 수를 반환한다. 예를 들어:
$cnt = chmod 0755, ‘file1’, ‘file2’;
는 $cnt를 몇 개의 파일의 변경되었는가에 따라 0, 1 혹은 2로 값을 설정한다.(이는 연산이 성공적으로 진행되었가의 여부를 의미하며, 결코 파일 퍼미션의 값이 변경되었는가의 여부가 아니다). 다음은 흔히 사용하는 예의 하나이다:
chmod 0755, @executables;
만약 사용자가 어떤 파일이 변경이 불가능한지를 알고자 한다면, 다음과 같이 하면 된다.
@cannot = grep {not chmod 0755, $_} ‘file1’, ‘file2’, ‘file3’;
die “$0: could not chmod @cannot\n” if @cannot;
앞의 예는 chmod 함수가 성공적으로 동작하지 못한 리스트의 각 요소들만을 골라내기 위해서 grep 함수를 사용했다.

chomp
chomp VARIABLE
chomp LIST
chomp
이 함수는 문자열의 맨 뒤 글자가 아니라, $/의 현재 값에 해당하는 글자가 문자열이 맨 뒤에 있는 경우에만 그 글자를 제거한다는 측면에서 chop 보다 좀 더 안전하게 동작한다. chop과는 달리 chomp는 제거된 문자열 수를 반환한다. 만약 $/가 특정 값을 갖지 않으면(패러그래프 모드에서), chomp는 선택된 문자열(strings)의 모든 줄바꿈 문자를 제거한다.(물론 인자를 LIST로 사용하는 경우).

chop
chop VARIABLE
chop LIST
chop
이 함수는 문자열(string)의 맨 마지막 글자를 지우며, 그 결과물로 맨 마지막 글자를 준다. chop 연산자는 주로 입력된 레코드의 마지막 글자를 지우는데 사용한며, 대치 연산자를 이용한 s/\n$//보다 훨씬 효율적이다. 만약 VARIABLE를 인자로 사용하지 않으면, 함수는 $_변수에 저장된 것에 대해 chop 연산을 한다. 예를 들면
while (<PASSWD>) {
    chop; # avoid \n on last field
    @array = split /:/;
    …
}
만약 LIST를 인자로 사용하면 리스트의 각 문자열(string)에 대해 chop 연산이 행해진다.
@lines = `cat myfile`;
chop @lines;
사용자는 다음과 같이 대치 연산이 행해지는 lvalue에 대해서도 chop 연산자를 사용할 수 있다.
chop($cwd = `pwd`);
chop($answer = <STDIN>);
한편, 다음과 같은 경우에는 위와 다른 결과를 낳게됨을 주의해야 한다.
$answer = chop($tmp = <STDIN>); # 잘못된 예
위의 경우 chop 연산자는 남아있는 문자열(string)이 아니라(남는 것은 $tmp에 저장된다), 제거되는 문자열을 반환하므로 개행 문자를 $answer 변수에 저장한다. 만약 맨 마지막 글자를 제거하고 남은 값을 저장하고자 한다면 다음과 같이 substr을 사용한다.
$answer = substr <STDIN>, 0, -1;
그러나, 위와 같은 결과를 얻기 위해 다음과 같이 사용한다.
chop($answer = <STDIN>);
한 글자 이상의 글자를 제거하려면 널 문자열이 할당된 lvalue로 substr을 사용한다. 다음은 $caravan에 저장된 문자열(string)의 맨 마지막 다섯 글자를 제거한다.
substr($caravan, -5) = “”;
위 예에서 5앞에 붙여진 ‘-‘는 substr 연산시 문자열(string)의 맨 마지막에서부터 5글자에 대해 작업이 수행되도록 해준다.

chown
chown LIST
이 함수는 파일로 구성된 리스트의 소유자(그리고 그룹)를 변경한다. 리스트의 첫 2개의 인자는 반드시 수치값을 가지는(numerical) uid와 gid이어야 하며, 순서도 지켜져야 한다. 이 함수는 성공적으로 처리된 파일의 개수를 반환한다. 예를 들면:
$cnt = chown $uid, $gid, ‘file1’, ‘file2’;
는 변수 $cnt를 0,1 혹은 2로 설정하게 된다. 값의 결정은 몇 개의 파일이 변경되었는지에 따라 달라 진다.(그 연산이 성공적이라는 관점이지, 소유자가 작업후 바뀌었는지의 여부가 관점이 아니다.) 여기에 좀더 일반적인 사용예가 있다.
chown $uid, $gid, @filenames;
다음은 사용자가 원하는 것을 찾아 chown 연산을 하는 서브루틴의 예이다:
sub chown_by_name {
    local($user, $pattern) = @_;
    chown((getpwnam($user))[2,3], glob($pattern));
}
&chown_by_name(“fred”, “*.c”);
이 예제는 각 파일의 그룹을 패스워드 파일에서 읽어온 gid로 변경하고 있음을 주의깊게 보아야 한다. 또다른 연산으로는 gid를 -1로 설정하면 파일의 그룹을 변화시키지 않게 된다.
대부분의 시스템에서는 사용자가 수퍼유저(root)가 아니면 파일의 소유권을 변경하는 것이 불가능하다. 단지 사용자가 2차 그룹에 속해있다면 파일의 1차 그룹을 2차 그룹으로 변경하는 것은 가능하다. 보안이 덜 중요한 시스템에서는 이러한 제약이 어느 정도 완화될 수 있지만, 그것이 다른 시스템에도 적용될 수 있다는 것은 아니다.

chr
chr NUMBER
이 함수는 문자 집합(set)의 각 NUMBER로 표현되는 문자열을 반환한다. 예를 들어 chr(65)는 ASCII 문자인 “A”이다. 여러 개의 문자를 변환하려면 pack(“C*”, LIST)을 사용한다.

chroot
chroot FILENAME
이 함수는 시스템 호출 chroot와 같은 기능을 한다. chroot(2)를 참고하라. 만약 성공이면 FILENAME은 현재 프로세스에 대해 새로운 루트 디렉토리가 된다. “/”로 시작하는 경로명의 시작점.
이 디렉토리는 exec 호출간에도 상속되면, 모든 서브프로세스에 의해 상속된다. chroot 연산을 취소하는 것은 불가능하다. 수퍼유저만이 이 함수를 사용할 수 있다. 다음은 많은 FTP 서버가 하는 일을 개략적으로 표현한 코드의 예이다.
chroot +(getpwnam(‘ftp’))[7]
or die “Can’t do anonymous ftp: $!\n”;

close
close FILEHANDLE
이 함수는 파일, 소켓 혹은 파일핸들과 관련된 파이프를 닫는다. 만약 사용자가 열려있는 파일핸들을 다시 열고자 하면 자동적으로 닫은 후, 다시 파일핸들을 열게 되므로 닫을 필요가 없다. 그러나, 입력 파일을 명시적으로 닫게 되면 행의 개수를 저장하는 변수($.)를 리셋하나, open 문에 의해 암묵적으로 입력 파일이 닫히게 되면 변수($. )는 리셋되지 않는다.
또한, 파이프를 닫으면 프로세서가 그 파이프 연산을 완료하기까지 기다리며,(만약 사용자가 그 연산뒤의 파이프 출력을 원하면) 그리고 그것은 스크립트가 파이프라인이 끝나기전에 끝나는 것을 방지한다. 명시적으로 파이프를 닫으면 파이프상에 실행되는 명령어의 상태값을 $?에 저장한다. 예를 들면:
open OUTPUT, ‘|sort >foo’; # 정렬할 파이프
close OUTPUT; # 정렬이 끝날 때까지 기다린다
die “sort failed” if $?; # 정렬이 되었는지 조사한다
open INPUT, ‘foo’; # 정렬 결과를 얻는다
파일핸들은 실제 파일핸들 이름을 값으로 주는 식일수도 있다. 이 함수는 새로운 객제 지향 입출력 패키지의 반환값인 파일핸들 객체의 레퍼런스일 수도 있다.

closedir
closedir DIRHANDLE
이 함수는 opendir에 의해 열려진 디렉토리를 닫는다. opendir의 예제를 참고하라.

connect
connect SOCKET, NAME
이 함수는 connect 시스템 호출과 비슷한 역할을 한다. connect(2)를 참조. 이 함수는 accept(2)를 수행하는 다른 대기 프로세스와 연결을 시작한다. 만약 성공적이면 참 값을, 그렇지 않으면 거짓 값을 반환한다(그리고 오류 코드를 $!에 저장한다). NAME은 소켓의 적절한 팩형 네트웍 주소이어야 한다. 예를 들면:
connect S, $destadd
or die “Can’t connect to $hostname: $! \n”;
소켓의 연결을 단절하려면 close 혹은 shutdown을 사용한다. 또한 6장의 “소켓” 부분의 예제를 참고하라.

cos
cos EXPR
이 함수는 EXPR(단위는 라디안)의 코사인값을 반환한다. 예를 들면, 다음 스크립트는 단위가 도인 각도(angle)인 코사인 테이블을 출력한다:
# 다음은 단위가 도로 표시된 값을 라디안으로 변환하는 쉬운 방법이다.
$pi = atan2(1,1) * 4;
$piover180 = $pi/180;
# 테이블 출력
for ($_ = 0; $_ <= 90; $_++) {
    printf “%3d %7.5f\n”, $_, cos($_ * $piover180);
}
코사인 값을 역으로 변환하고자 하면 POSIX::acos() 함수를 사용하거나 다음과 같이 하면 된다:
sub acos { atan2( sqrt(1 – $_[0] * $_[0]), $_[0] ) }

crypt
crypt PLAINTEXT, SALT
이 함수는 crypt(3)와 같은 방법으로 문자열(string)을 암호화한다. 이 함수는 어설픈 암호를 패스워드 파일에서 찾아내는데 유용한다.[2] 특권을 가진 사람만이 이것을 할 수 있다.
변수 $guess에 입력된 패스워드가 파일에 저장된 패스워드 $pass(가령 /etc/passwd )와 일치하는지를 알기 위해서는 다음과 같이 하면 된다.
if (crypt($guess, $pass) eq $pass) {
    # guess is correct
}
암호화된 패스워드를 추정하는 것은 가능하나 복호화하는 쉬운 방법은 없음에 유의해야 한다. 그리고, 비록 crypt(3)의 매뉴얼페이지에 대한 검색 역시 같은 행위라고 생각할 수 있지만 단어 salt를 2개의 글자로 자르는 것 역시 CPU시간을 소비하는 것이다.
다음의 예제는 사용자가 프로그램을 실행시켜서 자신의 패스워드를 알아낼 수 있는 프로그램이다.
$pwd = (getpwuid $<)[1];
$salt = substr $pwd, 0, 2;
system “stty -echo”;
print “Password: “;
chop($word = <STDIN>);
print “\n”;
system “stty echo”;
if (crypt($word, $salt) ne $pwd) {
    die “Sorry…\n”;
} else {
    print “ok\n”;
}
물론 누군가가 당신의 패스워드가 무엇인가를 묻는다고 해서 알려주는 것은 현명하지 못한일이다.
함수 crypt는 많은 데이터를 부호화하는데 사용하기에는 부적합하다. 따라서 많은 데이터를 부호화하는데 적합한 PGP(혹은 그와 비슷한 것)용 라이브러리 모듈을 찾아보는 것이 현명한 일이다.

dbmclose
dbmclose HASH
이 함수는 DBM 파일과 해쉬간이 바인딩을 깨뜨린다. 이 함수는 사실상 적절한 인자와 함께 untie하기 위한 하나의 호출이다. 그러나, Perl의 구버전과 호환성을 제공하기 위해 존재한다.

dbmopen
dbmopen HASH, DBNAME, MODE
이 함수는 DBM 파일을 해쉬(즉 조합배열)에 바인딩한다.(DBM은 Data Base Management의 약자이며, 해슁 알고리듬을 이용해 레코드에 무작위로 접근할 수 있도록 해 주는 C 라이브러리 루틴들로 구성되어 있다.) 인자 HASH는 해쉬(%로 표기가 시작되는)의 이름이다. 인자 DBNAME는 데이터베이스 (.dir 혹은 .pag가 붙지 않는)이름이다. 만약 인자 DBNAME이 사용되지 않고, 적절한 인자 MODE가 사용되면, 보호 모드값이 MODE(umask로 변경이 가능하다)인 데이터베이스가 생성된다. 명기된 이름의 데이터베이스가 존재않음에도 불구하고 신규로 데이터베이스가 생성되는 것을 방지하려면 undef으로 정의된 MODE를 명기하면 된다. 그럴 경우 명기된 데이터베이스가 존재하지 않으면 그 함수는 거짓값을 반환한다. 사용자의 시스템이 구버전의 DBM 함수만을 지원하면 사용자 프로그램에는 하나의 dbmopen 함수만을 사용할 수 있다. 함수 dbmopen을 호출하기 전에 해쉬에 할당된 값은 접근 불가능하다.
만약 사용자가 DBM 파일에 쓰기 가능하지 않다면, 사용자는 해쉬 변수를 단지 읽기 가능할 뿐이고 값을 설정할 수는 없다. 만약 그 파일에 쓸 수 있는지의 여부를 알고자 한다면 파일 테스트를 사용하거나 eval에서 더미 배열 항목 값을 설정하고자 하면 오류를 발생하는 기능을 이용하면 된다. keysvalues와 같은 함수는 대형 DBM 파일에 사용되는 경우 대형 리스트 값을 반환한다는 사실을 유의해야 한다. 어쩌면 사용자는 대형 DBM 파일을 사용하는 경우 함수를 반복적으로 사용할 수도 있다. 다음 예는 sendmail을 사용하는 시스템에서 메일 앨리어스(aliases)를 출력한다:
dbmopen %ALIASES, “/etc/aliases”, 0666
or die “Can’t open aliases: $!\n”;
while (($key,$val) = each %ALIASES) {
    print $key, ‘ = ‘, $val, “\n”;
}
dbmclose %ALIASES;
DBM 파일에 바인딩된 해쉬는 DBM 파일처럼 같은 한계를 지니고 있으며, 특히 사용자가 하나의 버킷(bucket)에 담을 수 있는 양에 대한 제한은 한계를 가진다. 만약 짧은 키와 값 만을 사용하면 거의 문제가 되지 않는다. 명심해야 할 또 다른 것은 기존의 많은 DBM 데이터베이스는 널(Null)로 끝나는 키와 값을 내포한다는 것이며, 이는 C 프로그램으로 구현되었기 때문이다. B 뉴스의 히스토리 파일과 구 버전인 sendmail aliases 파일이 그 예에 속한다. 단지 $key대신에 “$key”을 사용하면 된다.
일반적인 DBM 파일을 위한 내장 방식(build-in way)은 현재 없다. 혹자는 이것이 일종의 버그라고 생각할 수 있다. 그러나, DB_파일 모듈은 전체 파일에 대해 세부적으로 locking을 제공한다. 상세한 내용은 7장의 그 모듈에 관한 문서를 참고하라.
이 함수는 적절한 인자를 이용하여 tie 함수를 호출한 것과 같으나, 구버전인 Perl과 호환성을 제공하기 위한 것이다.

defined
defined EXPR
이 함수는 EXPR가 실수값인지의 여부를 불린(boolean) 값 형태로 반환한다. 유효한 문자열(string), 수치, 레퍼런스 값을 포함하지 않은 스칼라는 정의되지 않은 값 혹은 줄여서 undef으로 알려져 있다. 많은 연산들이 파일의 끝, 초기화되지 않은 변수, 시스템 오류 등, 예외적인 조건하에서 정의되지 않은 값을 반환한다. 사용자가 실수 널 문자열(string)을 반환하는 연산자를 사용할 때, 이 함수를 이용하면 정의되지 않은 널 문자열(string)과 정의된 널 문자열(string)을 구별할 수 있다.
사용자는 배열, 해쉬 혹은 서브루틴등이 메모리를 할당 받았는지 여부를 조사할 수 있다. 배열과 해쉬는 어떤 값이 입력된 경우에 메모리가 할당되나 서브루틴의 경우 정의된 내용이 성공적으로 파싱되면 메모리가 할당된다. 사전 정의된 특수 변수에 defined를 사용한다고 해서 직관적인 결과를 얻을 수 있지는 않다. 다음은 해쉬 변수가 스칼라 값을 가지는지의 여부를 조사하는 일부 코드이다.
print if defined $switch{‘D’};
이 처럼 해쉬 요소에 대해 이 함수를 사용할 때, defined를 통해서 만이 그 값이 정의되어 있는지의 여부, 키가 해쉬 테이블의 항목인지의 여부를 알 수 있다. 기존의 해쉬 키가 정의되지 않은 스칼라 값을 가지는 것이 가능하다. 해쉬 키가 존재하는지의 여부는 exists를 사용하면 알 수 있다. 다음 예는 데이터가 부족한 경우 정의되지 않은 값을 몇몇 연산 반환값으로 준다는 사실을 이용한 것이다.
print “$val\n” while defined($val = pop(@ary));
앞의 예처럼 다음은 시스템 호출시 오류 값이 반환값으로 주어지는 경우이다:
die “Can’t readlink $sym: $!”
unless defined($value = readlink $sym);
패키지의 심볼 테이블은 해쉬(조합 배열) 형태로 저장되므로 다음과 같이 패키지의 존재 여부를 조사하는 것이 가능하다.
die “No XYZ package defined” unless defined %XYZ::;
마지막으로 존재하지 않는 서브루틴을 조사하여 호출을 하지 않는것도 가능하며, 다음은 그 예이다:
sub saymaybe {
    if (defined &say) {
        say(@_);
    }
    else {
        warn “Can’t say”;
    }
}
또한 undef도 참조하시오.

delete
delete EXPR
이 함수는 지정된 해쉬의 특정 키와 그 값을 지운다.(이 함수는 파일을 지우지는 않는다. 파일을 지우기 위해서는 unlink를 참조하시오.) 해쉬 변수의 하나인 $ENV{}에서 특정 값을 지우면 사용자 환경이 변화하게 된다. (쓰기 가능한)DBM 파일과 연결된 해쉬에서 특정 키와 값을 지우면 DBM 파일의 특정 항목를 지우는 것이 된다.
다음의 단순한 예제는 해쉬의 모든 값을 비효율적으로 지운다.
foreach $key (keys %HASH) {
    delete $HASH{$key};
}
(undef를 사용하는 것이 훨씬 빠르다)
최종 연산이 해쉬 키 검색이면 EXPR은 매우 복잡해질 수 있다:
delete $ref->[$x][$y]{$key};
일반적인 해쉬에 대해서는 delete는 지워진 값(키가 아니라)을 반환한다. 그러나, DBM 파일에 연결된 해쉬, 가령 tie된 해쉬는 반드시 앞의 경우와 같지는 않다. 해쉬 요소가 지워졌는지의 여부를 알기위해서는 exists를 사용한다.

die
die LIST
이 함수가 eval 문 밖에서 사용되었다면 LIST에 연결된 값들을 STDERR에 출력하며, $!(errno)의 현재값을 출력하며 끝낸다. 만약 $!가 0이면 ($? >> 8)의 값을 가지면서 종료한다.(그것은 파이프 혹은 [CWR]`명령어`[CWR]`상에 system, wait, close문에서 발생한 마지막 파생 차일드 프로세서의 상태이다.) 만약 ($? >> 8)이 0이면 그것은 255로 끝난다. 만약 LIST가 생략되면 변수 $@의 현재값이 혹시 존재하면 전파된다. 그렇지 않으면 문자열(string) “Died”가 디폴트로 사용된다.
동일한 예:
die “Can’t cd to spool: $!\n” unless chdir ‘/usr/spool/news’;
chdir ‘/usr/spool/news’ or die “Can’t cd to spool: $!\n”
(위 예제에서 후자가 주로 사용된다. 왜냐하면 chdir이 더 중요한 부분이므로 앞에 두는 것을 선호하는 것이다.)
eval 문내에서는 함수 die는 변수 $@를 오류 메시지로 설정하며, 그리고 eval 문을 중지시키고 그 결과 정의되지 않은 값을 반환한다. 함수 die는 프로그램내에서 상위 eval 문에서 발생할 수 있는 지명된(named) 예외를 지적해낼 수 있다. 이 장의 후반부에서 소개되는 eval 함수 부분을 참고하라. 만약 LIST의 최종 값이 개행문자로 끝나지 않으면 현재 스크립트 파일이름, 행 번호 그리고 입력 행 번호(혹시 존재하면)등이 개행문자와 더불어 메시지에 추가된다. 힌트: 문자열(string) “at scriptname line 123” 이 메시지에 추가될 때, 가끔 메시지에 “, stopped”이 추가되면 사용자가 보다 이해를 쉽게 할수 있다. 가령 사용자가 스크립트 파일인 canasta를 실행한다고 가정해보자:
die “/etc/games is no good”;
die “/etc/games is no good, stopped”;
앞의 예는 다음을 각각 생성한다:
/etc/games is no good at canasta line 123.
/etc/games is no good, stopped at canasta line 123.
만약 오류 메시지에 파일 이름과 행 번호를 함께 나타내기를 원하면 __FILE__와 __LINE__인 특수 토큰(token)을 사용한다:
die ‘”‘, __FILE__, ‘”, line ‘, __LINE__, “, phooey on you! \n”;
앞의 코드는 다음의 메시지를 생성한다:
“canasta”, line 38, phooey on you!
exitwarn을 참고하라.

do
do BLOCK
do SUBROUTINE(LIST)
do EXPR
do BLOCKBLOCK내의 일련의 명령어를 수행하며, 마지막 명령어를 수행 후 그 값을 반환한다. 루프 변형자에 의해 변형된 경우 Perl은 루프 조건을 조사하기 전에 BLOCK을 일단 실행한다.(다른 문에서는 루프 변형자가 먼저 조건문을 조사한다.)
do SUBROUTINE(LIST)은 서브루틴의 또 다른 호출 형태이다. 2장의 “서브루틴”을 참고하라.
do EXPREXPR을 파일 이름으로 사용하며 그 파일의 문들을 일종의 Perl 스크립트처럼 실행한다. 주로 Perl 서브루틴 라이브러리에서 각종 서브루틴을 포함해 사용한다. 그 결과
do ‘stat.pl’;
는 다음과 같다:
eval `cat stat.pl`;
그러나, 앞의 문이 훨씬 효율적이고, 파일 이름에 대한 오류 메시지를 주며 배열 @INC에 저장된 모든 디렉토리를 검색한다는 점이 뒤의 문과 다르다.(2장의 “특수 변수” 부분을 참고하라.) 그러나, 호출할 때마다 매번 파싱(parsing)한다는 점에서 앞의 두 문은 같다. 따라서, 사용자는 루프내에서 이러한 것을 하길 원하지는 않을 것이다.
라이브러리 모듈을 사용하려면 userequire를 사용하는 것이 좋다는 것을 명심하라. 왜냐하면 이들 연산자는 오류 검색과 문제가 존재하면 오류를 발생시키기 때문이다.

dump
dump LABEL
dump
이 함수는 즉시 코어 덤프를 발생시킨다. 주로 이것은 프로그램 시작부분에서 사용자의 모든 변수를 초기화 한 후, undump(1)를 사용하여 코어 덤프를 실행이진 파일 변환시키기 위해 사용된다.(undump 프로그램은 Perl과 함께 제공되지 않으며, 특정 아키텍쳐에서는 그 기능이 가능하지 않다. 하나의 대안으로 GNU unexec()를 사용하는 방법도 있다.) 향후 다른 방법이 지원될 것이다. 새로운 이진 파일이 실행될 때에는 goto LABEL를 실행하면서 시작한다.
그 연산은 간섭하는 코어덤프와 재실행을 하는 goto 문으로 생각하라. 만약 LABEL이 생략되면 함수는 프로그램이 맨 처음부터 다시 시작하도록 프로그램을 조정한다. 덤프시에 열려진 모든 파일은, Perl 입장에서 혼란이 발생할 가능성이 존재하면서 그 프로그램이 다시 실행되더라도 더 이상 열리지 않는다는 점을 유의해야 한다. 명령어의 -u 스위치에 관해서도 참고하라.
예를 들면:
#!/usr/bin/perl
use Getopt::Std;
use MyHorridModule;
%days = (Sun => 1, Mon => 2, Tue => 3, Wed => 4, Thu => 5, Fri => 6, Sat => 7, );
dump QUICKSTART if $ARGV[0] eq ‘-d’;
QUICKSTART:
Getopts(‘f:’);
앞의 시동(startup) 코드는 약간 느리게 초기화가 진행되며, dump 함수를 호출하여 프로그램의 바로 그 순간 상태를 출력한다. 그 프로그램의 코어 덤프된 버전이 실행되면 시동 코드 전체부분을 그냥 넘어가고 직접 QUICKSTART 레이블부터 실행한다. 만약 원래의 스크립트가 -d 스위치를 사용하지 않고 실행되면, 그 프로그램의 수행은 중단되고, 다시 정상적으로 수행된다.
만약 사용자가 사용자의 프로그램의 실행속도를 증가시키기 위해 dump 문을 사용하고자 하면, 6장의 Perl 순수-코드 컴파일러와 8장 기타 사항의 효율성에 관한 부분을 참고하라. 외견상으로는 더 빠르게 실행하도록 해주는 오토로딩(autoloading)에 대해서도 고려해 보아야 한다.

each
each HASH
이 함수는 해쉬의 다음 값에 대한 키와 값으로 구성되는 2 요소 리스트를 반환한다. 연이은 성공적인 each 호출을 통해 사용자는 전체 해쉬에 대해 작업을 할 수 있다. 해쉬의 항목들은 무작위 순서로 반환된다. 해쉬의 항목들이 다 읽혀지면 최종적으로 널 리스트가 반환값으로 주어진다.(리스트 할당문에서 사용될 때에는 거짓값이 반환값으로 주어진다).
each 문을 호출한 후 다시 호출하면 새로운 순환을 시작하는 것이다. 순환연산자는 해쉬의 모든 요소를 읽거나 스칼라 구문의 함수 keys를 호출함으로써 초기화가 가능하다.
일련의 함수 each를 호출함으로써 해쉬 전체에 대해 순환할 수 있다. 항목(entries)은 무작위 순서로 반환된다. 사용자가 해쉬에 대해 순환 연산을 하는 동안에는, 비록 해쉬에 대해 delete 문을 사용하는 것은 가능하지만, 해쉬에 요소를 절대로 더해서는 안된다.
스칼라 구문에서는 each는 키값을 반환하지만 그 값이 거짓인지를 유심히 살펴보아야 한다.
프로그램의 모든 each, keys 그리고 values 함수 호출이 공유하는 각 해쉬에 대한 순환연산자가 존재한다. 즉 keysvalues 호출후 each를 호출하면 처음부터 시작한다. 다음 예제는 출력 순서는 다르지만 printenv(1) 프로그램처럼 사용자 환경값을 출력한다:
while (($key,$value) = each %ENV) {
    print “$key=$value\n”;
}
또한 keysvalues을 참고하라.

eof
eof FILEHANDLE
eof()
eof
이 함수는 FILEHANDLE 값을 읽어서 그 결과 파일의 끝이거나 FILEHANDLE을 열수 없는 경우 참값을 반환한다. 실제로 파일핸들 이름을 값으로 주는 식이FILEHANDLE일 수도 있다. 인자가 없는 eof 문은 마지막 읽은 파일의 end-of-file 상태를 반환한다. 빈 괄호 ()는 명령어상의 나열된 결합된 파일과 관련하여 사용된다. 다시 말해 while (<>) 루프에서 eof()는 파일 그룹의 맨 마지막 파일의 끝부분을 검색한다. while (<>) 루프에서 각 파일을 테스트하려면 eof(ARGV)나 eof를 사용한다. 예를 들면, 다음의 코드는 마지막 파일의 맨 마지막 행 바로 앞에 ‘-‘를 입력한다:
while (<>) {
    if (eof()) {
        print “-” x 30, “\n”;
    }
print;
}
한편, 다음 스크립트는 입력 파일의 행번호 매김을 초기화한다.
while (<>) {
    print “$.\t$_”;
    if (eof) { # eof()가 아님
        close ARGV; # 초기화 $.
    }
}
sed 프로그램의 “$”처럼, eof는 행 번호의 범위를 보여준다. 다음은 각 입력 파일의 /pattern/에서부터 끝까지 그 내용을 출력하는 스크립트이다:
while (<>) {
    print if /pattern/ .. eof;
}
플립-플롭 연산자(..)는 각 행의 정규 표현식의 일치 여부를 조사한다. 패턴이 일치할 때까지 연산자는 거짓값을 반환한다. 패턴이 최종적으로 일치하는 경우, 연산자는 참값을 반환하기 시작하며, 그 행들을 출력한다. 연산자 eof가 최종적으로 참값을 반환하는 경우(검사하는 파일의 맨 끝에서), 플립-플롭 연산자는 리셋하며 거짓값을 반환하기 시작한다.
함수 eof는 사실 1 바이트를 읽은 후 ungetc(3)를 이용하여 입력 스트림에 그것을 저장하며, 그러므로 상호작용하는 문장에서 그 함수는 그렇게 유용하지 않다는 것을 유의해야 한다. 다양한 입력 연산자는 이미 while-루프 조건문내에 아주 잘 동작하므로, 사실 경험많은 Perl 프로그래머는 거의 eof를 사용하지 않는다. 2장의 foreach에 대한 설명 예제를 참고하라.

eval
eval EXPR
eval BLOCK
EXPR은 하나의 작은 Perl 프로그램처럼 전체 프로그램에서 분리되어 실행된다. 즉 현재의 Perl 프로그램 안에서 실행되는 것이며, 특정 값으로 설정된 변수는 그 이후에도 그 값이 존속되며 서브루틴 혹은 포맷 정의도 존속한다. eval 문은 하나의 블록으로 취급되며 eval 문내에 선언된 지역 변수는 eval 문이 끝날 때까지 존속한다.(localmy를 참조하시오)
블록내의 다른 코드들처럼 최종 세미콜론(semicolon)은 필요하지 않다.
EXPR이 주어지지 않으면 eval은 $_를 인자로 취급한다. eval의 반환값은 서브루틴처럼 eval 문내의 마지막 식의 처리 값이다. 그리고 사용자는 eval 문의 중간에 return 연산자를 사용하여 반환값을 받을 수도 있다. 만약 구문 오류나 실행시(run-time) 오류(die 연산자 실행 결과로 발생하는 모든 것들을 포함하여)가 발생하면, eval은 정의되지 않은 값을 반환하고 오류 메시지를 $@에 저장한다. 오류가 없는 경우 $@에 널 문자열이 저장된다. 따라서, 사용자는 eval 문의 수행시 오류가 발생하는지를 알 수 있다. 다음은 실행시에 선택된 해쉬 배열에 어떤 요소를 설정하는 문이다.
eval “\$$arrayname{\$key} = 1”;
(사용자는 소프트 레퍼런스를 이용하여 좀 더 쉽게 구현할 수 있다. 4장의 “심볼릭 레퍼런스(Symbolic Reference)”를 참고하라.) 그리고 다음은 간단한 Perl로 구현한 쉘의 한 예이다.
while (<>) { eval; print $@; }
eval 문은 트랩을 발생하거나, 그렇지 않으면 치명적 오류를 발생하므로, 그 것은 특정 기능(가령 socket 혹은 symlink)이 지원되는지의 여부를 결정하는데 유용하다. 사실상 eval 문은 Perl에서 모든 예외사항 처리시에 사용한다. 만약 실행되는 코드가 변환하지 않으면, 사용자는 런타임(run-time) 오류를 발생시키기 위해서는 eval BLOCK 문을 사용한다; 블록안의 코드가 매번 실행될 때 마다 컴파일되는 것이 아니라 한번만 컴파일되면 훨씬 효율성이 좋은 것이다. 그리고 오류값이 $@에 저장된 형태로 반환되기도 한다. 예제:
# divide by zero오류 발생. 치명적이지는 않음.
eval { $answer = $a / $b; }; warn $@ if $@;
# 위와 같지만 조금 비효율적
eval ‘$answer = $a / $b’; warn $@ if $@;
# 컴파일 시간 오류(트랩되지 않음)
eval { $answer = };
# 런타임 오류
eval ‘$answer =’; # $@를 설정
다음 예제에서 컴파일 단계를 성공적으로 마치기 위해서는 BLOCK내의 코드가 문법에 맞는 Perl 코드여야 한다. 문자열(string)내의 코드는 실행시까지 검사되지 않으며, 그 결과 실행시(run-time)에 오류가 발생한다. 다음의 예제에서 eval 문의 사용시 어떤 결과가 나올지에 대해 사용자는 주의하여 기억해야 할 것이다:
eval $x; # 사례 1
eval “$x”; # 사례 2
eval ‘$x’; # 사례 3
eval { $x }; # 사례 4
eval “\$$x++”; # 사례 5
$$x++; # 사례 6
위의 사례 1과 2는 동일하게 동작한다. 즉 변수 $x에 저장된 코드를 실행한다.(사례 2는 잘못된 인용 부호 “를 사용하고 있으며 이로 인해 독자로 하여금 인용부호가 없는 경우 다른 연산결과를 낳게 될지 모른다는 생각을 하도록 한다. 모든 경우에 변수 $x 의 내용은 파싱(parsing)을 위해 일단 문자열(string)로 변환되어야 한다.) 사례 3과 4역시 마찬가지로 같은 동작을 한다. 즉 코드 $x를 실행하며, $x의 값을 반환하는 일 이외에는 어떤것도 실행하지 않는다.(사례 4의 경우 그 식이 매번 재 컴파일될 필요가 없으므로 사례 3보다 더 나은 방법이다.) 사례 5는 변수명을 치환(interpolation)하기 위해 이중 인용부호를 사용할 수 있는 경우이나 사례 6의 경우처럼 그 대신에 심볼릭 레퍼런스를 사용할 수 있는 특수한 경우라는 점을 제외하고는 같은 경우이다.
흔히 질문하는 내용으로는 종료 루틴을 어떻게 설정해야 하는 것이다. 흔히 하는 방법은 END 블록을 사용하는 것이다. 그러나, 사용자는 다음과 같이 eval 문을 사용하여 할 수도 있다:
#!/usr/bin/perl
eval <<‘EndOfEval’; $start = __LINE__;
.
. # your ad here
.
EndOfEval
# Cleanup
unlink “/tmp/myfile$$”;
$@ && ($@ =~ s/\(eval \d+\e) at line (\d+)/$0 .
” line ” . ($1+$start)/, die $@);
exit 0;
텍스트의 내용이 변하지 않았다면 eval 문의 코드는 다시 컴파일되지 않는다는 사실에 유의해야 한다. 가령 사용자가 다시 강제로 컴파일하고자(예를 들면 사용자가 .. 연산자의 리셋을 원하기 때문에)하는 아주 드문 경우에는 사용자는 다음과 같이 할 수 있다:
eval $prog . ‘#’ . ++$seq;

exec
exec LIST
이 함수는 현재 실행되고 있는 Perl 스크립트를 끝내고 그 대신에 다른 프로그램을 실행시킨다. 만약 LIST내에 2개 이상의 인자가 있으면(혹은 LIST가 2개 이상의 값을 가진 배열이면), 함수는 LIST의 인자와 함께 C의 execvp(3)루틴을 호출한다. 이 함수는 모든 쉘 명령어의 처리를 거치지 않게 한다. 만약 스칼라 인자가 단지 하나이면 그 인자가 쉘 메타 문자인지의 여부가 조사된다. 만약 메타 문자가 발견되면 전체 인자는 파싱(parsing)하기 위해 “/bin/sh -c”는 전달된다.[3] 만약 메타 문자가 없으면, 효율성 측면에서 인자는 워드 단위로 쪼개어져 직접 execvp(3)에 전달된다. 왜냐하면 그렇게 하므로써 쉘 프로세싱의 모든 오버헤드(overhead)를 피할 수 있기 때문이다. 일반적으로 exec는 결코 그 함수를 호출한 위치로 실행 위치로 돌아오지 않는다 만약 호출 위치로 돌아온다 하더라도 항상 거짓값을 반환하며, 사용자는 무엇이 잘못되었는지를 알기 위해서 $!를 조사해야 한다. 그리고 exec(그리고 system)는 사용자의 출력 버퍼를 지우지 않는다는 점과 출력 내용을 잃어버리지 않기 위해 1개 혹은 그 이상의 파일핸들을 $|와 연결시켜 명령어 버퍼링을 할 필요가 있다는 점을 유의하라. 다음 문은 echo 프로그램을 실행하여 현재 인자 리스트의 내용을 출력한다.
exec ‘echo’, ‘Your arguments are: ‘, @ARGV;
다음 예제는 사용자가 파이프라인 과정을 실행하기 위해 exec를 사용한다:
exec “sort $outfile | uniq”
or die “Can’t do sort/uniq: $!\n”;
유닉스 execv(3) 호출은 프로그램으로 하여금 실행된 그 프로그램의 이름을 알 수 있도록 해 준다. 이 이름은 사용자가 실행하기 위해 OS에게 준 프로그램 이름과는 상관이 없을 수도 있다. 디폴트로 Perl은 LIST의 첫번째 요소를 단순히 복사하여 두가지 목적으로 사용한다. 그러나, 만약 사용자가 LIST의 첫번째 요소가 실행되는 것을 원하지 않고, 그 실행되는 프로그램이 자신의 이름에 관한 거짓 정보를 주고자 한다면 사용자는 얼마든지 그렇게 할 수 있다. 먼저 사용자가 실행시키고자 하는 프로그램의 진짜 이름을 변수에 저장한 후, print 문에 사용되는 파일핸들 처럼 콤마(,)없이 LIST의 맨 앞에 그 변수를 놓으면 된다.(그렇게 하면 LIST를 여러개의 값을 가지는 리스트로 해석된다. 비록 리스트에는 단일 스칼라만이 존재하더라도.) 그러면 LIST의 첫번째 요소는 실행되는 프로그램의 이름으로 해석되는 것이다. 예를 들면:
$shell = ‘/bin/csh’;
exec $shell ‘-sh’, @args; # pretend it’s a login shell
die “Couldn’t execute csh: $!\n”;
임의의 코드를 내포하는 블록을 이용하여 프로그램 이름을 저장하고 있는 스칼라를 단순히 교체할 수 있다. 다음은 위의 예제를 그렇게 단순화한 것이다:
exec {‘/bin/csh’} ‘-sh’, @args; # pretend it’s a login shell

exists
exists EXPR
이 함수는 지정된 해쉬 키가 해쉬 테이블에 존재하면, 비록 해당하는 값이 정의되지 않는다 하더라도, 참값을 반환한다.
print “Exists\n” if exists $hash{$key};
print “Defined\n” if defined $hash{$key};
print “True\n” if $hash{$key};
해쉬 요소는 정의되어 있는 경우에만 참일 수 있으며, 만약 요소가 존재하면 단지 정의되어 있다고 할 수 있다. 그러나, 어느 경우에도 그 역은 반드시 참일 수는 없다. 최종 연산이 해쉬 키 검색인 경우 EXPR은 매우 복잡해 질 수 있다.
if (exists $ref->[$x][$y]{$key}) { … }
exit
exit EXPR
이 함수는 EXPR를 평가한 후 그 값을 반환값으로 하여 종료한다. 다음은 사용자가 x 혹은 X를 입력하면 프로그램을 종료하는 코드의 한 예이다.
$ans = <STDIN>;
exit 0 if $ans =~ /^[Xx]/;
만약 EXPR이 생략되면 그 함수는 상태값 0을 가지고 종료한다. 서브루틴이 어떤 종류의 오류를 발생시킬 가능성이 있다면 사용자는 exit 문을 사용하여 서브루틴을 중지시켜서는 안된다. 대신에 eval 문에서 발생가능한 오류 코드와 함께 die 문을 사용하라.

exp
exp EXPR
이 함수는 eEXPR 지수승을 반환한다. 만약 EXPR이 생략되면 exp($_)를 반환한다. 일반적인 지수 함수의 표기에는 **연산자를 사용한다.

fcntl
fcntl FILEHANDLE, FUNCTION, SCALAR
이 함수는 유닉스의 fcntl(2)함수를 호출한다.(fcntl는 “file control”를 의미한다.) 사용자는 다음과 같이 선언해야 한다:
use Fcntl;
즉 먼저 정확한 함수 정의를 해야 한다. SCALARFUNCTION에 따라 읽혀지거나 그리고/혹은 씌여진다. SCALAR의 문자열(string) 값을 지시하는 포인터는 실제 fcntl 호출의 세번째 인자로 전해진다.(만약 SCALAR가 문자열(string) 값을 가지지 않고 수치(numeric)값을 가지면, 그 값은 문자열(string)값의 포인터가 아니라 직접 전해진다.)
함수 fcntl (그리고 ioctl)의 반환값은 다음과 같다:

시스템 반환값

Perl 반환값

-1

정의되지 않은

0

문자열 “0이지만 참값

-1 0 제외한 임의의

숫자

즉 Perl은 성공일때는 참을 그리고 실패일 때는 거짓을 반환한다. 그러나, 사용자는 OS가 반환하는 실제 값을 쉽게 결정할 수 있다:
$retval = fcntl(…) or $retval = -1;
printf “System returned %d \n”, $retval;
앞의 예를 보면 %d 포맷으로 인해 시스템 호출 결과인 반환값은 0이지만, 문자열(string) “0 이지만 참”이 출력된다. 예를 들면, Perl은 항상 파일 기술자의 close-on-exec 플래그 값을 2보다 큰 값으로 설정하므로, 만약 사용자가 파일 기술자 3을 서브프로세스에 전달하고자 하면 다음의 예제처럼 플래그를 지워야 할 것이다:
use Fcntl;
open TTY,”+>/dev/tty” or die “Can’t open /dev/tty: $!\n”;
fileno TTY == 3 or die “Internal error: fd mixup”;
fcntl TTY, &F_SETFL, 0
or die “Can’t clear the close-on-exec flag: $!\n”;
만약 fcntl(2)가 사용자 컴퓨터에서 지원되지 않으면, 함수 fcntl 호출은 치명적(fatal) 오류를 생성한다. 만약 사용자 컴퓨터에서 지원되면 사용자는 close-on-exec 플래그의 수정, non-blocking 입출력 플래그 수정, lockf(3) 함수의 에뮬레이션등과 같은 일을 할 수 있고, 입출력이 결정되지 않은 경우 SIGIO 시그널을 받을 수 있도록 할 수 있다. 사용자는 심지어 record-locking의 기능(facilities)도 가질 수 있다.

fileno
fileno FILEHANDLE
이 함수는 파일핸들의 파일 기술자를 반환한다.(파일 기술자는 심볼인 패일핸들과는 달리 작은 값의 정수이다.) 만약 파일핸들이 열리지 않으면 반환값으로 undef을 준다. 이 함수는 select용 비트맵을 만드는 데 유용하며, 만약 syscall(2)을 지원하면 애매모호한 특정 시스템 호출에 인자를 전달하는데 유용하다. 또한 open 함수가 사용자에게 원하는 파일 기술자를 주는지의 여부를 이중으로 확인하는데 유용하다. fcntl 문의 예제를 참고하라.
만약 FILEHANDLE이 하나의 식이면, 그 식의 값이 간접적인 이름이나 혹은 직접적인 파일핸들 객체의 레퍼런스로 파일핸들을 나타낸다.
주의 사항: 프로그램이 실행되는 동안에 Perl의 파일핸들과 수치(numeric) 파일 기술자의 상관 관계를 믿지 마라. 만약 파일이 닫히고 다시 열리면, 그 파일 기술자는 변한다. 파일핸들인 STDIN, STDOUT 그리고 STDERR는 0, 1, 2인 파일 기술자(유닉스 표준 관례)로 시작한다. 그러나, 만약 사용자가 여러 번 파일핸들을 닫고 다시 열면 그 파일 기술자 값은 변한다. 그러나, 만약 사용자가 항상 파일을 닫은 후 바로 열게되면 파일 기술자 값을 0, 1, 2를 사용해도 문제가 없다. 왜냐하면 유닉스 시스템에서는 기본 규칙이 사용가능한 현재의 가장 작은 값(사용자가 닫은 직후의 그 값 가운데)을 기술자 값으로 사용하기 때문이다.

flock
flock FILEHANDLE, OPERATION
이 함수는 FILEHANDLE에 대해 flock(2)를 호출한다. 만약 OPERATION의 정의에 대해 궁금하면 flock(2)의 매뉴얼 페이지를 참고하라. 만약 flock을 실행하는 경우 사용자의 컴퓨터에 flock(2)이 지원되지 않으면 치명적 오류가 발생하게 되므로 그렇다면 다른 잠금 메커니즘을 이용해 flock을 에뮬레이션 해야 한다. 다음은 BSD 기반의 시스템에서 제공되는 전자함(mailbox)에 추가 기능을 구현한 예이다.
$LOCK_SH = 1;
$LOCK_EX = 2;
$LOCK_NB = 4;
$LOCK_UN = 8;
sub lock {
    flock MBOX, $LOCK_EX;
    # and, in case someone appended
    # while we were waiting…
    seek MBOX, 0, 2;
}
sub unlock {
    flock MBOX, $LOCK_UN;
}
open MBOX, “>>/usr/spool/mail/$ENV{‘USER’}”
or die “Can’t open mailbox: $!”;
lock();
print MBOX $msg, “\n\n”;
unlock();
flock 문은 네트웍 파일 시스템(NFS)를 통해 접근하는 파일에 대해서는 동작하지 않는다는 것을 유의해야 한다.

fork
fork
이 함수는 fork(2)를 호출한다. 만약 성공적으로 실행되면 그 함수는 모(parent) 프로세스에게 자 프로세스(child pid)를 반환값으로 주며 자 프로세스에게는 0을 반환한다.(만약 그 실행이 실패하면 모(parent) 프로세스에게 정의되지 않은 값을 반환한다. 이때 자 프로세스는 존재하지 않는다.)
플러쉬(flush, 버퍼를 비우다: 역자 주)되지 않은 버퍼는 그 둘 프로세스 과정에도 여전히 플러쉬(flush)되지 않는다는 점을 유의해야 한다. 그것은 사용자가 프로그램의 앞 부분에서 $|를 1개 이상의 파일핸들에 설정하여 출력을 중복되게 복사하는 것을 피할 필요가 있다는 것이다. “fork를 할수 없음” 오류를 조사하는 과정에 자 프로세스를 시작하는 가장 안전한 방법은 다음과 같다:
FORK: {
    if ($pid = fork) {
        # 모(parent) 프로세스가 여기
        # 자(child) 프로세스 pid가 $pid에 저장되어 있다
    } elsif (defined $pid) { # $pid가 정의되어 있으면 0
        # 자(child) 프로세스
        # 모(parent) 프로세스 pid는 getppid로 얻을 수 있다
    } elsif ($! =~ /No more process/) {
        # EAGAIN, 복구할 수 있는 fork오류
        sleep 5;
        redo FORK;
    } else {
        # 운명의 fork 오류
        die “Can’t fork: $!\n”;
    }
}
이처럼 system, 역인용부호(backquotes), 또는 파일핸들로 프로세스를 여는 fork(2)연산을 반드시 주의할 필요는 없다. 왜냐하면 Perl은 그런 경우 일시 실패하더라도 fork를 자동적으로 재실행한다. 자(child) 코드를 exit 문을 사용하여 끝내는 경우에는 주의해야 한다. 그렇지 않으면, 자(child) 프로세스는 의도한 바와 달리 조건문을 떠나 모(parent) 프로세스의 실행 코드 부분이 실행된다.
만약 사용자가 자신의 자 프로세스를 fork하면, 사용자는 자 프로세스가 죽을 때, 그 좀비 프로세스에 대해 wait를 해야 한다. 이에 대한 예제는 함수 wait 부분을 참고하라.
만약 POSIX 규격을 준수하지 않으면, 함수 fork는 유닉스와 같은 OS(혹은 유사한)가 아니면 지원되지 않는다.

format
format NAME =
그림 행(picture line)
값 리스트(value list)

.
write 함수에 의해 사용될 이름을 가진 그림 행(picture line)을 선언한다. 만약 NAME이 생략되면, 디폴트로 그 이름은 STDOUT 파일핸들의 디폴트 포맷 이름인 STDOUT가 사용된다.
서브 선언처럼 이것은 전역에 걸친 선언이며, 컴파일시에 실행된다. 값 리스트(value list)에 사용되는 모든 변수는 포맷 선언시에 보여져야 한다. 다시 말해, 렉시컬리(lexically) 특정 영역에 속하는 변수들은 파일의 앞단부분에서 선언되어야 한다. 반면에 동적으로 특정 영역에 속하는 변수는 단지 wirte 문을 호출하는 루틴에서 선언되면 된다. 다음은 관련된 예제이다(여기서 우리는 이미 변수 $cost와 $quantity를 계산했다고 가정한다).
my $str = “widget”; # 문법적으로 전역에 걸쳐 선언된 변수
format Nice_Output =
Test: @<<<<<<<< @||||| @>>>>>
$str, $%, ‘$’ . int($num)
.
$~ = “Nice_Output”; # 포맷을 선택
local $num = $cost * $quantity; # 동적으로 전역에 걸쳐 선언된 변수
write;
파일핸들처럼 포맷 이름은 심볼 테이블(패키지)에 존재하는 식별자가 되며, 패키지 이름으로 충분히 사용할 수 있다. 심볼 테이블의 항목중 하나인 타입글로브(typeglobs)내에서 포맷은 그 자신의 이름공간내에 존재하며, 그 이름공간은 파일핸들, 디렉토리 핸들, 스칼라, 배열, 해쉬 혹은 서브루틴과는 또다른 것이다. 그러나, 6가지 다른 종류처럼 어떤 이름의 포맷이라도 어떤 타입글로브(typeglob)에 대해 local문에 의해서 영향을 받을 수 있다. 다시 말해 포맷은 다른 장치(gadgets)와는 별도로 타입글로브(typeglob)내에 존재하는 또하나의 장치(gadgets)인 것이다.
2장의 “포맷” 부분에서는 다양하고 상세한 사용 방법과 예제들을 보여준다. 2장의 “Perl 파일핸들 특수 변수”와 “전역 특수 변수” 부분에서 내장 특정 포맷 변수에 대해 설명한다. 그리고, 7장의 English와 FileHandle 모듈을 이용하면 내자 특정 포맷 변수에 쉽게 접근 가능하다.

formline
formline PICTURE, LIST
사용자가 이 함수를 호출할 수도 있지만, 이 함수는 포맷에 사용되는 내장 함수이다. 이 함수는 PICTURE의 내용에 따라 출력 값들을 포맷 출력 누산기(accumulator)인 $^A에 저장하여 정형화(format)한다. 그에 따라 write 문이 끝난 후 $^A의 내용이 특정 파일핸들에 씌여지나 사용자는 $^A를 직접 읽을 수도 있고 $^A를 “”로 설정할 수도 있다. 함수 format은 대개 폼 라인 하나당 한 개의 formline을 사용하나, 함수 formline 그 자체는 PICTURE 안에 몇 개의 개행문자가 포함되어 있는지를 개의치 않는다는 점을 유의해야 한다. 이것은 ~와 ~~ 토큰이 전체 PICTURE를 하나의 행으로 취급한다는 것을 의미한다. 따라서, 사용자는 포맷 컴파일러처럼 하나의 레코드-포맷을 구현하기 위해서는 여러 개의 formline을 사용해야 한다.
만약 picture 주위에 인용부호(“)를 사용할 때에는 주의해야 한다. 왜냐하면 @는 배열 이름을 의미하는 첫 글자로 이해되기 때문이다. formline은 항상 참값을 반환한다. 다른 예제에 대해서는 2장의 “포맷”을 참고하라.

getc
getc FILEHANDLE
getc
이 함수는 FILEHANDLE로 지정된 입력을 통해 바이트 단위로 정보를 읽어들인후 그 것을 반환한다. 파일 맨 끝에서는 널 문자열(string)을 반환한다. 만약 FILEHANDLE을 인자로 사용하지 않으면 함수는 STDIN에서 정보를 읽어들인다. 이 연산자는 수행속도가 매우 느리나, 가끔 키보드로부터 입력되는 버퍼링된 단일 문자를 읽을 경우 매우 유용하다. 이 함수는 단일 문자 입력시에는 사용하지 못한다. 만약 버퍼링 되지 않은 입력으로부터 읽어들일 경우 사용자는 OS의 기능을 이용한 약간 더 세련된 방법을 사용해야 한다:
if ($BSD_STYLE) {
    system “stty cbreak </dev/tty >/dev/tty 2>&1”;
} else {
    system “stty”, “-icanon”, “eol”, “”;
}
$key = getc;
if ($BSD_STYLE) {
    system “stty -cbreak </dev/tty >/dev/tty 2>&1”;
} else {
    system “stty”, “icanon”, “eol”, “^@”; # ASCII NUL
}
print “\n”;
앞의 코드는 단말기상에 입력된 문자를 문자열 변수 $key에 저장한다. 만약 사용자의 stty 프로그램이 cbreak와 같은 선택 사양을 가지고 있으면, 사용자는 $BSD_STYLE이 참값을 가지는 코드를 사용해야 한다. 그렇지 않다면 사용자는 BSD_STYLE이 거짓값을 가지는 코드를 사용해야 한다. 여기서는 stty에 대한 선택사양을 결정하는 것은 독자들의 연습문제로 남기고자 한다. 7장의 POSIX 모듈은 POSIX::getattr() 함수를 사용하여 이 함수보다 이식성이 나은 버전을 제공한다. 또한 사용자는 가까운 CPAN 배포처(FTP나 WWW 서비스를 통해)에서 TERM::ReadKey 모듈을 참고하라.

getgrent
getgrent
setgrent
endgrent
이들 함수는 비슷한 이름을 가진 시스템 라이브러리 루틴과 같은 역할을 한다 — getgrent(3)을 참고하라. 이들 루틴은 /etc/group 파일(혹은 어딘가의 어떤 서버의 비슷한 그 무엇)에 대해 반복 검색을 한다. 함수 getgrent의 리스트 형태의 반환값은 다음과 같다:
($name, $passwd, $gid, $members)
여기서 변수 $members는 /etc/group 파일에 적힌 로그인 이름 리스트(스페이스로 분리되어 있음)를 포함한다. 그룹 이름을 gid로 변환하기 위한 해쉬를 만들려면 다음과 같이 한다:
while (($name, $passwd, $gid) = getgrent) {
    $gid{$name} = $gid;
}
스칼라 구문에서 getgrent는 단지 그룹 이름을 반환한다.

getgrgid
getgrgid GID
이 함수는 getgrgid(3)와 같은 기능을 한다: 즉 그룹 번호별로 그룹 파일 항목을 검색한다. 리스트 구문의 반환값은:
($name, $passwd, $gid, $members)
여기서 변수 $members는 /etc/group 파일에 적힌 로그인 이름 리스트(스페이스로 분리되어 있음)를 포함한다. 만약 사용자가 이것을 반복하고자 하면 getgrent 문을 이용하여 해쉬(조합 배열) 데이터를 캐쉬하는 것을 고려해야 한다.
스칼라 구문에서 getgrgid는 단지 그룹 이름을 반환한다.

getgrnam
getgrnam NAME
이 함수는 getgrnam(3)처럼 같은 기능을 한다: 즉 그룹 이름별로 그룹 파일 항목을 검색한다. 리스트 구문의 반환값은 :
($name, $passwd, $gid, $members)
여기서 변수 $members는 /etc/group 파일에 적힌 로그인 이름 리스트(스페이스로 분리되어 있음)를 포함한다. 만약 사용자가 이것을 반복하고자 하면 getgrent 문을 이용하여 해쉬에 데이터를 슬러핑(slurping)하는 것을 고려해야 한다.
스칼라 구문에서 getgrnam는 단지 numeric(숫자) 그룹 ID를 반환한다.

gethostbyaddr
gethostbyaddr ADDR, ADDRTYPE
이 함수는 gethostbyaddr(3)과 같은 기능을 한다. 즉 팩형 이진 네트웍 주소를 해당하는 이름(그리고 또다른 주소)로 변환한다. 리스트 구문의 반환값은 :
($name, $aliases, $addrtype, $length, @addrs)
@addrs는 팩형 이진 주소의 리스트이다. 인터넷 도메인에서는 각 주소는 4 바이트로 구성되면 다음과 같이 unpack 함수를 사용하여 얻을 수 있다:
($a, $b, $c, $d) = unpack(‘C4’, $addrs[0]);
스칼라 구문에서 gethostbyaddr는 단지 호스트 이름을 반환한다. 또 다른 각도에서 이해하려면 6장의 “소켓” 부분을 참고하라.

gethostbyname
gethostbyname NAME
이 함수는 gethostbyname(3) 같은 역할을 한다. 즉 네트웍 호스트 이름을 해당하는 주소(그리고 그 밖의 이름)로 변환한다. 리스트 구문 형태의 반환값은 :
($name, $aliases, $addrtype, $length, @addrs)
여기서 @addrs는 어드레스 목록으로 구성된 리스트이다. 인터넷 도메인에서 주소는 4 바이트로 구성이 되며 다음과 같이 unpack 함수를 사용하여 스칼라 값을 얻을 수 있다.
($a, $b, $c, $d) = unpack(‘C4’, $addrs[0]);
스칼라 구문에서 gethostbyname는 단지 호스트 주소를 반환한다. 또 다른 각도에서 이해하려면 6장의 “소켓” 부분을 참고하라.

gethostent
gethostent
sethostent STAYOPEN
endhostent
이들 함수는 비슷한 이름을 가진 시스템 라이브러리 루틴들과 같은 역할을 한다 — gethostent(3)을 참고하라. 이들은 /etc/hosts 파일을 검색하여 한번에 하나의 항목을 반환한다. gethostent의 반환값은:
($name, $aliases, $addrtype, $length, @addrs)
앞의 예제에서 @addrs는 가공되지 않은(raw) 주소 리스트이다. 인터넷 도메인에서 각 주소는 4바이트 길이이며 다음과 같이 하여 unpack할 수 있다:
($a, $b, $c, $d) = unpack(‘C4’, $addrs[0]);
이 루틴을 사용하는 스크립트는 이식성이 있다고 생각해서는 안된다. 만약 기계가 네임서버를 사용하면 연결된 지구상의 모든 기계의 주소에 대한 정보 요청을 만족시키기 위하여 인터넷의 기계들에 대해 정보 요청을 한다. 그래서 이 루틴은 그런 기계에서는 지원되지 않는다.

getlogin
getlogin
이 함수는 /etc/utmp에서 현재의 사용자 ID를 반환한다. 만약 그렇지 않고 널(Null) 을 반환하면 getpwuid를 사용한다.
$login = getlogin || (getpwuid($<))[0] || “Intruder!!”;

getnetbyaddr
getnetbyaddr ADDR, ADDRTYPE
이 함수는 getnetbyaddr(3)와 같은 역할을 한다: 즉 네트웍 주소를 해당하는 네트웍 이름 혹은 이름들로 변환한다. 리스트 구문 형태의 반환값은:
($name, $aliases, $addrtype, $net)
스칼라 구문에서 getnetbyaddr는 단지 네트웍 이름을 반환한다.

getnetbyname
getnetbyname NAME
이 함수는 getnetbyname(3)와 같은 역할을 한다: 즉 네트웍 이름을 해당하는 주소로 변환한다. 리스트 구문 형태의 반환값은 :
($name, $aliases, $addrtype, $net)
스칼라 구문에서 getnetbyname는 단지 네트웍 주소를 반환한다.

getnetent
getnetent
setnetent STAYOPEN
endnetent
이들 함수는 비슷한 이름을 가진 시스템 라이브러리 루틴들과 같은 역할을 한다 – getnetent(3)을 참고하라. 이들 함수는 /etc/networks 파일 혹은 그와 비슷한 것에 대해 역할을 수행한다. 리스트 형태의 반환값은:
($name, $aliases, $addrtype, $net)
스칼라 구문에서 getnetent는 네트웍 이름을 반환한다.

getpeername
getpeername SOCKET
이 함수는 SOCKET 연결시 상대방이 팩형 소켓 주소를 반환한다. 예를 들면:
use Socket;
$hersockaddr = getpeername SOCK;
($port, $heraddr) = unpack_sockaddr_in($hersockaddr);
$herhostname = gethostbyaddr($heraddr, AF_INET);
$herstraddr = inet_ntoa($heraddr);

getpgrp
getpgrp PID
이 함수는 명시된 PID를 가지는 현재의 프로세스 그룹을 반환한다.(현재의 프로세스에 대한 PID는 0을 사용한다) 만약 getpgrp(2)를 지원하는 않는 시스템에서 getpgrp를 사용한다면 치명적인 오류가 발생한다. PID를 생략한 경우, 이 함수는 현재 프로세스에 대한 프로세스 그룹을 반환한다. POSIX getpgrp(2) 시스템 호출을 이용하여 이 연산자를 구현하는 시스템에서는 PID를 생략해야 하며, 그렇지 않다면 0이 되어야 한다.

getppid
getppid
이 함수는 모 프로세스의 프로세스 ID(PID)를 반환한다. 대부분의 UNIX 시스템에서 사용자의 모 프로세스 ID(PID)가 1로 바뀌면, 사용자의 모(parent) 프로세스는 죽게되며, 사용자는 init 프로그램에 의해 이양된다.

getpriority
getpriority WHICH, WHO
이 함수는 프로세스, 프로세스 그룹, 사용자의 현재 우선권(priority)을 검색한다. getpriority(2)를 참고하라. getpriority(2)를 지원하지 않는 시스템에서 getpriority를 사용하면 치명적인 오류가 발생된다. 현재 프로세스의 우선권을 검색하고 싶으면 다음과 같이 한다.
$curprio = getpriority(0, 0);

getprotobyname
getprotobyname NAME
이 함수는 getprotobyname(3)과 같은 역할을 한다: 즉 프로토콜 이름을 해당하는 번호로 변환한다. 리스트 구문 형태의 반환값은:
($name, $aliases, $protocol_number)
스칼라 구문에서 getprotobyname 프로토콜 번호를 반환한다.

getprotobynumber
getprotobynumber NUMBER
이 함수는 getprotobynumber(3)와 같은 역할을 한다: 즉 프로토콜 번호를 해당하는 이름으로 변환한다. 리스트 구문 형태의 반환값은:
($name, $aliases, $protocol_number)
스칼라 구문에서 getprotobynumber는 프로토콜 이름을 반환한다.

getprotoent
getprotoent
setprotoent STAYOPEN
endprotoent
이들 함수는 비슷한 이름을 가진 시스템 라이브러리 루틴들과 같은 역할을 한다. – getprotent(3)을 참고하라. 함수 getprotoent의 반환값은:
($name, $aliases, $protocol_number)
스칼라 구문에서 getprotoent는 프로토콜 이름을 반환한다.

getpwent
getpwent
setpwent
endpwent
이들 함수는 비슷한 이름을 가진 시스템 라이브러리 루틴들과 같은 역할을 한다. – getpwent(3)을 참고하라 이들 루틴은 /etc/passwd 파일(혹은 어딘가의 어떤 서버의 비슷한 그 무엇)에 대해 반복 검색을 한다. 리스트 구문의 반환값은:
($name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell)
어떤 기계는 디스크 영역 할당 기능과 설명 필드를 다른 목적으로 사용하기도 하나, 나머지 필드는 항상 같아야 한다. 로긴 이름을 uid로 변환하기 위해 해쉬를 설정하기 위해 다음과 같이 한다:
while (($name, $passwd, $uid) = getpwent) {
    $uid{$name} = $uid;
}
스칼라 구문에서 getpwent는 사용자 이름을 반환한다.

getpwnam
getpwnam NAME
이 함수는 getpwnam(3)와 같은 역할을 한다: 즉 사용자 이름에 해당하는 패스워드 파일 항목을 검색한다. 리스트 구문 형태의 반환값은:
($name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell)
만약 사용자가 이것을 반복하고자 하면 getpwent 이용하여 해쉬(조합 배열) 데이터를 캐쉬하는 것을 고려해야 한다.
스칼라 구문에서 getpwnam은 사용자 고유의 숫자 ID를 반환한다.

getpwuid
getpwuid UID
이 함수는 getpwuid(3)와 같은 역할을 한다: 즉 사용자 고유의 숫자 ID에 해당하는 패스워드 항목을 검색한다. 리스트 구문 형태의 반환값은:
($name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell)
만약 사용자가 이것을 반복하고자 하면 getpwent를 이용하여 해쉬에 데이터를 슬러핑(slurping) 하는 것을 고려해야 한다.
스칼라 구문에서 getpwuid는 사용자 이름을 반환한다.

getservbyname
getservbyname NAME, PROTO
이 함수는 getservbyname(3)와 같은 역할을 한다: 즉 서비스(포트) 이름에 해당하는 포트 번호를 검색한다. PROTO는 “tcp”와 같은 프로토콜 이름이다. 리스트 구문 형태의 반환값은:
($name, $aliases, $port_number, $protocol_name)
스칼라 구문에서 getservbyname는 서비스 포트 번호를 반환한다.

getservbyport
getservbyport PORT, PROTO
이 함수는 getservbyport(3)와 같은 역할을 한다: 즉 서비스 포트 번호에 해당하는 서비스 이름을 검색한다. PROTO는 “tcp”와 같은 프로토콜 이름이다. 리스트 구문 형태의 반환값은:
($name, $aliases, $port_number, $protocol_name)
스칼라 구문에서 getservbyport는 서비스 포트 이름을 반환한다.

getservent
getservent
setservent STAYOPEN
endservent
이들 함수는 비슷한 이름을 가진 시스템 라이브러리 루틴과 같은 역할을 한다 – getservent(3)을 참고하라. 이 들 함수는 /etc/services 파일 혹은 그와 같은 기능을 하는 파일에 대해 반복하여 작업을 한다. 리스트 구문 형태의 반환값은:.
($name, $aliases, $port_number, $protocol_name)
스칼라 구문에서 getservent는 서비스 포트의 이름을 반환한다.

getsockname
getsockname SOCKET
이 함수는 SOCKET 연결의 한 쪽 끝의 주소를 반환한다.(일반적으로 여러분은 여러분의 주소를 이미 알고 있지 않은가? 왜냐하면 시스템과 소켓을 연결하기 위해 소켓에 와일드카드를 포함한 주소를 바운드하고 소켓 연결을 받기 때문이다. 또는 사용자가 모(parent) 프로세스에 의해 소켓을 전달 했기 때문일 수도 있다. – 예를 들면, inetd.)
use Socket;
$mysockaddr = getsockname(SOCK);
($port, $myaddr) = unpack_sockaddr_in($mysockaddr);

getsockopt
getsockopt SOCKET, LEVEL, OPTNAME
이 함수는 요청한 소켓 선택사양을 반환한다. 만약 오류가 있으면 정의되지 않은 값을 반환한다. 좀 더 상세한 정보는 setsockopt을 참고하라.

glob
glob EXPR
이 함수는 쉘의 기능처럼 파일 이름의 확장과 함께 EXPR의 값을 반환한다.(만약 EXPR이 생략되면 $_이 대신 사용된다.) 이 함수는 다음 예제에서 보듯이 타이핑이 쉽다는 점을 제외하고는 <*> 연산자의 기능을 구현한 내장 함수이다. 다음 2개의 결과를 비교해 보라:
@result = map { glob($_) } “*.c”, “*.c,v”;
@result = map <${_}>, “*.c”, “*.c,v”;
함수 glob는 여러 개의 항목들을 나타내기 위해서는 *를 사용한다는 점을 제외하고는 Perl의 typeglob 개념과는 관련이 없다.

gmtime
gmtime EXPR
이 함수는 time 함수의 반환값인 시간을 9개의 요소로 구성되는 그리니치 시간대의 시간인 리스트로 변환한다.(GMT혹은 UTC 또는 특정 문화에서는 Zulu로 표기되고, 그러나 Zulu가 줄루 문화를 포함한다는 의미는 아니다). 대개 다음과 같이 사용된다:
($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
gmtime(time);
모든 리스트 요소들은 숫자이며 구조 tm(그것은 C 프로그래밍 구조이다. 그것을 단언하지는 말라)으로 부터 나온 것이다. 특히 이것은 변수 $mon가 0..11의 값을 가진다는 것을 의미하며, 변수 $wday는 0..6의 값을 그리고 년도는 변수 $year로부터 1900을 빼야 됨을 의미한다.(월과 요일명을 내포한 배열의 서브스크립트(subscript)가 항상 0으로 시작하므로 사용자는 어떤 것이 0으로 시작하는 지를 기억해야 한다.) 만약 EXPR이 생략되면 그것은 gmtime(time)이다. 예를 들어 런던의 현재 달을 출력하기 위해서는:
$london_month = (qw(Jan Feb Mar Apr May Jun
Jul Aug Sep Oct Nov Dec))[(gmtime)[4]];
Perl 라이브러리 모듈 Time::Local은 역으로 변환하는 기능을 가진 서브루틴 timegm()을 포함한다. 스칼라 구문에서 gmtimectime(3)을 반환한다 – GMT 시간에 근거한 문자열(string)처럼.

goto
goto LABEL
goto EXPR
goto &NAME
goto LABELLABEL로 지정된 문을 찾아 거기서부터 실행한다. 초기화가 필요한 서브루틴이나 foreach 루프등의 내부로 실행문이 옮겨지는 것은 허용되지 않는다. 이것은 최적으로 활용하고자 하는 구조 안으로 들어가는 것에는 사용할 수 없다. 서브루틴 외부로 나가는 경우도 포함하여 동적인 영역내에서는 어디로든지 실행점이 이동할 수 있다. 그러나, 서브루틴 외부로 이동하는 경우에는 lastdie와 같은 다른 구조를 사용하는 것이 더 좋은 방법이다. Perl을 만든 이는 결코 이러한 형태의 goto를 사용할 필요를 못 느꼈다(Perl에서 그렇다는 것이지 C 언어에서는 또 다른 문제다).
좀 더 극단적인 이야기를 하자면(그리고 아주 어리석은) Perl은 goto EXPR의 형태를 허용한다. 이는 EXPR이 처리되어 레이블 명의 역할을 할 수 있는 무엇인가를 도출하는 것을 전제한다. 그리고 이러한 형태의 구성은, 그 문이 컴파일 당시에는 LABEL이 무엇인지를 알 수 없기 때문에 실행시까지는 그 영역이 결정되지 않는다. 이러한 것은 FORTRAN에서 계산된 goto 문을 허용하나, 이런 형태는 권하기에는 좋지 않다. 만약 사용자가 관리성을 최적화 하고자 하면
goto +(“FOO”, “BAR”, “GLARCH”)[$i];
goto &NAME는 현재 실행되는 서브루틴 대신에 지명된 서브루틴을 호출하는 것으로 대치하여 상당히 마술처럼 보인다. 이것은 다른 서브루틴을 로딩하여 이 서브루틴원래의 것이 아니고–이 마치 처음으로 호출된 것처럼 동작하는 AUTOLOAD 서브루틴에 의해 사용된다 (원래의 서브루틴에서 @_를 변경하는 것이 교체 서브루틴에게 파급되는 효과를 제외하고는). goto 문이후에는 원래의 루틴이 맨먼저 호출되었는지의 여부는 호출한 측에서도 판별할 수 없다.

grep
grep EXPR, LIST
grep BLOCK LIST
이 함수는 LIST의 각 요소들에 대하여 차례로 변수 $_에다 각 요소를 임시로 저장하면서, 불린 문의 형태로 EXPR이나 BLOCK을 처리하며. 리스트 구문의 형태로 그 함수는 식의 값이 참값인 각 요소들의 리스트를 반환한다.(그 연산자는 파일상의 특정 패턴과 일치하는 행을 추출하는 기능을 가진 자주 이용하는 유닉스 프로그램의 이름를 본따서 만든 것이다. Perl 언어에서는 식이 종종 하나의 패턴이 되나, 반드시 그럴필요는 없다.) 스칼라 구문에서 grep문은 그 식이 참값인지의 여부의 횟수를 반환한다. @all_lines가 모든 라인을 가지고 있다고 가정하며, 다음의 예제는 주석이 달린 행을 제거한다.
@code_lines = grep !/^#/, @all_lines;
왜냐하면 $_는 리스트 값의 레퍼런스이므로 $_를 변경하게되면 원래의 리스트의 요소들을 변경하게 되는 것이다. 이것이 유용하며 또한 지원이 되지만 때때로 예기치 못한 황당한 결과를 초래할 수도 있다. 예를 들면:
@list = qw(barney fred dino wilma);
@greplist = grep { s/^[bfd]// } @list;
@greplist에는 “arney”, “red”, “ino”가 들어있지만 @list에는 “arney”, “red”, “ino”, “wilma”가 들어있다.
또한 map도 참고하라. 다음 2개의 문은 동일한 기능을 수행한다.
@out = grep { EXPR } @in;
@out = map { EXPR ? $_ : () } @in

hex
hex EXPR
이 함수는 주어진 EXPR을 16진수 문자열로 해석하여 그 결과를 10진수 값으로 준다.(0 혹은 0x로 시작하는 문자열을 해석하기 위해서는 oct를 참고하라.) 만약 EXPR이 생략되면 함수는 $_를 해석한다. 다음 코드는 $number에 4,294,906,560를 저장한다.
$number = hex(“ffff12c0”);
앞과 반대되는 함수를 사용하려면 다음과 같다:
sprintf “%lx”, $number;

import
import CLASSNAME LIST
import CLASSNAME
내장형(built-in) 함수는 지원되지 않는다. 그것은 use 연산자를 이용해 이름을 다른 모듈로 엑스포트(export)하고자 하는 모듈에 의해 정의된 일반적인 클래스 방식이다. 더 상세한 내용은 use를 참고하라.

index
index STR, SUBSTR, POSITION
index STR, SUBSTR
이 함수는 문자열 STR에서 첫번째로 일치하는 SUBSTR 문자열의 위치를 반환한다. 만약 POSITION이 명시되어 있으면 POSITION은 검색할 문자열의 시작 위치를 의미한다. 위치는 0에서 시작한다.(사용자는 변수 $[ 를 이용하여 시작 위치를 지정할 수 있으나 가급적이면 피하는 것이 좋다]. 만약 SUBSTR이 발견되지 않으면 그 함수는 베이스 값보다 1이 작은 값(대개 -1)을 반환한다. 문자열을 검색하기 위해서는 다음과 같이 할 수 있다:
$pos = -1;
while (($pos = index($string, $lookfor, $pos)) > -1) {
    print “Found at $pos\n”;
    $pos++;
}

int
int EXPR
이 함수는 EXPR의 정수 부분의 값을 반환한다. 만약 EXPR이 생략되었으면 $_를 사용한다. 종종 C 프로그래머들은 나누기를 할 경우 int를 사용하는 것을 잊어버린다. Perl에서 나누기 연산은 실수 연산이다:
$average_age = 939/16; # 58.6875로 계산됨 (C에서는 58)
$average_age = int 939/16; # 58로 계산

ioctl
ioctl FILEHANDLE, FUNCTION, SCALAR
이 함수는 시스템 호출인 ioctl(2)의 기능을 수행한다. 사용자는 다음과 같이 사용한다:
require “ioctl.ph”;
# /usr/local/lib/perl/ioctl.ph에서 찾을 수 있음.
먼저 정확한 함수 정의를 한다. 만약 ioctl.ph가 존재하지 않거나 혹은 그 정의가 정확하지 않은 경우 사용자는 <sys/ioctl.h>와 같은 C 헤더 파일에 근거하여 스스로 정의해야 한다.(인터넷에서 제공되는 Perl에서는 이와 같은 일을 도와주는 h2ph라는 스크립트를 지원하고 있으며 이것은 유용하다.) SCALARFUNCTION에 따라 읽혀지거나 혹은 쓰여진다 SCALAR의 문자열 값에 대한 포인터는 실제 ioctl(2) 호출의 3번째 인자로 전달된다.(만약 SCALAR가 문자열 값을 가지지 않고 수치 값을 가지면, 그 값은 문자열 값의 포인터가 아니라 그 값이 직접 전달된다.) 함수 packunpackioctl이 사용하는 구조의 값을 처리하는데 사용된다. 다음 예제는 유닉스 시스템에서 삭제 문자를 키로 설정하는 예이다 (좀 더 이식성이 좋은 인터페이스에 관한 7장의 POSIX 모듈을 참고하라):
require ‘ioctl.ph’;
$getp = &TIOCGETP or die “NO TIOCGETP”;
$sgttyb_t = “ccccs”; # 4 chars and a short
if (ioctl STDIN, $getp, $sgttyb) {
    @ary = unpack $sgttyb_t, $sgttyb;
    $ary[2] = 127;
    $sgttyb = pack $sgttyb_t, @ary;
    ioctl STDIN, &TIOCSETP, $sgttyb
    or die “Can’t ioctl TIOCSETP: $!”;
}
ioctl(그리고 fcntl)의 반환값은 다음과 같다.

시스템 호출 반환값

Perl 반환값

-1

정의되지 않은

0

문자열 “0이지만 참값

-1 0 제외한 임의의

숫자

따라서, Perl은 연산이 성공인 경우 참을, 실패인 경우 거짓을 결과값으로 생성한다. 한편, 사용자는 쉽게 OS가 주는 그 반환값을 결정할 수 있다.
$retval = ioctl(…) or $retval = -1;
printf “System returned %d\n”, $retval;
ioctl을 호출하는 것은 이식성을 보장하지 못한다. 가령 스크립트 파일 작성시 한번만 echo를 끄는 경우 다음과 같이 하는 것이 훨씬 이식성(속도도 느리지 않고)이 좋다.
system “stty -echo”; # 대부분의 UNIX에서 작동하는 예
이는 Perl이 무엇이든지 작성할 수 있다고 해서 모든 것을 Perl로 해결할 필요는 없는 것이다. Apostle Paul의 말을 인용하면 “모든 것이 허용된다고 해서 그 모든 것이 이롭다고 할 수는 없다.”

join
join EXPR, LIST
이 함수는 LIST의 각 문자열을 결합하여 하나의 문자열로 만들며, 이 때 필드는 EXPR를 사용하며, 만들어진 그 문자열을 결과로 돌려준다. 예를 들어
$_ = join ‘:’, $login,$passwd,$uid,$gid,$gcos,$home,$shell;
위와 반대되는 작업을 하기 위해서는 split를 사용한다. 만약 필드를 고정된 위치에 두어 문자열을 결합하기 위해서는 pack을 사용한다.
다수의 문자열을 결합하는 가장 효율적인 방법은 널 문자열을 이용하여 결합하는 것이다.

keys
keys HASH
이 함수는 해쉬의 키로 구성된 리스트를 돌려준다. 이때 키는 임의의 순서이나, valueseach함수가 생성하는 순서로 고정된다. (물론 해쉬가 시스템 호출중에 수정되지 않는다고 가정한 것이다) 다음은 사용자 환경 값을 출력하는 또 다른 방법이다.
@keys = keys %ENV;
@values = values %ENV;
while (@keys) {
    print pop(@keys), ‘=’, pop(@values), “\n”;
}
또는 키 값을 이용해서 정렬하는 경우에는 다음과 같다.
foreach $key (sort keys %ENV) {
    print $key, ‘=’, $ENV{$key}, “\n”;
}
배열 변수의 값을 정렬하는 경우 비교 함수를 사용하는 편이 좋다. 다음의 예는 내림차순으로 해쉬 변수의 값을 정렬한다..
foreach $key (sort { $hash{$b} <=> $hash{$a} } keys %hash)) {
    printf “%4d %s\n”, $hash{$key}, $key;
}
약간 큰 DBM 파일에 연결(binding)된 해쉬에 keys를 사용하면 약간 큰 리스트를 생성하며, 사용자가 약간 큰 프로세스를 가진다는 것을 유의해야 한다. 이 경우에 사용자는 each 함수를 사용하기를 원할 수 있다. each 함수는 해쉬의 항목을 하나의 거대한 리스트로 처리하는 것이 아니라, 각 항목을 하나씩 루프형태로 돌면서 처리한다. 스칼라 구문에서 keys는 해쉬의 요소 개수를 반환한다(그리고, each 문을 초기화한다). 그러나, DBM 파일을 포함한 해쉬에 대한 정보를 얻기 위해, Perl은 전체 해쉬에 대해 작업을 하며, 그러한 경우에는 그렇게 효율적이지 못하다.

kill
kill LIST
이 함수는 여러 개의 프로세스에 하나의 신호를 보낸다. 목록을 구성하는 첫번째 요소는 숫자(신호값)이다. 숫자 대신에 이름을 ”로 둘러싸서 사용할 수 있다(이때 신호 이름의 맨 앞에는 SIG를 사용하지 않는다). 함수의 반환값은 성공적으로 신호를 받은 프로세스의 개수이다. 만약 신호가 음수면 함수는 프로세스 대신에 프로세스 그룹을 죽인다. (시스템 V에서는 프로세스 숫자가 음수일 경우에도 프로세스 그룹을 죽이나 이식성이 없다)
$cnt = kill 1, $child1, $child2;
kill 9, @goners;
kill ‘STOP’, getppid; # Can *so* suspend my login shell…

last
last LABEL
last
명령어 last는 C언어의 break와 같은 역할을 한다(대개 루프에서 사용한다); 즉, 루프에서 last를 만나면 바로 루프를 종료한다.
만약 LABEL을 사용하지 않으면 가장 안쪽의 루프에서 빠져 나오게 된다. 블록안에 continue 문이 있더라도 실행되지 않는다.
LINE: while (<STDIN>) {
    last LINE if /^$/; # 헤더가 지나면 루프를 종료함
    # 루프의 나머지 부분
}

lc
lc EXPR
이 함수는 EXPR에 저장된 값을 소문자로 변환해 준다(만약 EXPR을 사용하지 않는다면 $_에 저장된 값을 변환한다). 이것은 이중 인용부호내의 문자열에 대해 \L 이스케이프를 구현한 내장 함수이다. POSIX setlocale(3) 설정과 관련이 있다.

lcfirst
lcfirst EXPR
이 함수는 EXPR에 저장된 값의 첫번째 문자를 소문자로 변환해 준다(만약 EXPR을 사용하지 않는다면 $_에 저장된 값을 변환한다). 이것은 이중 인용부호내의 문자열에 대해 \l 이스케이프를 구현한 내장 함수이다. POSIX setlocale(3) 설정과 관련이 있다.

length
length EXPR
이 함수는 스칼라 변수 EXPR의 길이를 반환한다. 만약 EXPR이 생략되었으면 $_의 길이를 반환한다. EXPR부분에 EXPR이 시작하는 것처럼 보이지 않을 경우에는 토큰 분석기는 혼란스러울 수 있다는 것에 주의한다. 때문에 EXPR은 항상 괄호안에 두는 것이 좋다.
배열이나 해쉬의 크기를 알기위해 length 함수를 이용하지 마라. 배열의 크기를 알려면 scalar함수를 이용하고, 해쉬의 크기를 알려면 scalar 함수와 keys 함수를 이용한다.(scalar는 거의 사용하지 않는다)

link
link OLDFILE, NEWFILE
이 함수는 OLDFILE에 연결된 NEWFILE을 만든다. 이 함수는 성공일 경우 1을 그렇지 않은 경우 0을(오류코드는 $!에 저장된다) 반환한다. 이장의 후반부에 설명되는 symlink도 참고하라.
이 함수는 비 유닉스 시스템에서는 지원되지 않는다.

listen
listen SOCKET, QUEUESIZE
이 함수는 listen(2) 시스템 호출과 같은 역할을 한다. 이 함수는 시스템에 소켓상의 연결요청을 받아들일 수 있으며, 시스템이 QUEUESIZE 만큼 대기연결을 queue할 수 있음을 나타낸다. 이는 전화를 걸려고 하는 사람 5명이 대기하고 있는 경우를 생각해 보면 쉽게 이해가 될 것이다. 이 함수는 성공적으로 수행된 경우 참을, 그렇지 않은 경우는 거짓을 반환한다(이 때 오류코드는 $!에 저장된다). 6장의 “소켓”을 참고하라.

local
local EXPR

이 연산자는 1개 이상의 전역 변수를 특정 내부의 블록, 서브루틴, eval, 혹은 파일에 한정하여 변수의 값을 제한되게 사용토록 해 준다. 만약 리스트상에 2개 이상의 변수가 사용되면 그 리스트는 반드시 괄호안에 써야 한다.

왜냐하면 연산자는 콤마보다 우선 순위가 앞서므로 맨 앞의 변수만 인자로 의미를 가지기 때문이다.

리스트상의 모든 변수는 반드시 값을 할당할 수 있는 lvaule여야 한다. 이 연산자는 감추어진 스택에 변수의 값을 저장한 후 블록, 서브루틴, eval, 혹은 파일에서 빠져 나온 후 그 값을 다시 환원시킨다.

local이 실행되고 그 영역에서 빠져 나오기 전에 어떤 서브루틴이 호출되면 그 서브루틴은 전역 변수의 값이 아닌 지역 변수의 값을 가지게 된다. 이는 전역변수이지만 값 자체는 local에 의해 지역변수의 값을 가지기 때문이다. 이러한 것을 기술적인 용어로 “dynamic scoping”이라 한다.

사용자가 원한다면 EXPR의 값을 초기화 할수 있다.(만약 초기화 값이 명시되지 않은 경우에는 모든 스칼라 변수는 정의되지 않는 임의의 값을 가지고 배열이나 해쉬는 값을 가지지 않게 된다.)

local은 대개 서브루틴의 인자를 공식적으로 선언하는데 사용된다. 일반적인 할당문 처럼, 만약 사용자가 왼쪽의 변수들을 괄호로 둘러싸면(혹은 변수가 배열이나 해쉬이면), 오른쪽의 식은 리스트 구문의 형태로 계산된다. 그렇지 않으면, 오른쪽의 식은 스칼라 구문 형태로 계산된다. 다음은 특정 범위의 숫자를 가지는 변수 $i에 따라 임의의 코드를 실행하는 루틴이다. 변수 $i의 범위는 eval 문 안으로 전파됨을 유의하라.
&RANGEVAL(20, 30, ‘$foo[$i] = $i’);
sub RANGEVAL {
    local($min, $max, $thunk) = @_;
    local $result = “”;
    local $i;
    # Presumably $thunk makes reference to $i
    for ($i = $min; $i < $max; $i++) {
        $result .= eval $thunk;
    }
    $result;
}
다음 코드는 전역 배열에 임시적인 변화를 가져오는 예이다.
if ($sw eq ‘-v’) {
    # 전역 배열을 지역 배열로 초기화
    local @ARGV = @ARGV;
    unshift @ARGV, ‘echo’;
    system @ARGV;
}
# 전역 배열 @ARGV 복구
사용자는 해쉬를 임시적으로 변경할 수도 있다:
# 해쉬 %digits에 임시로 몇가지 항목을 추가한 예
if ($base12) {
    # (주의: 이 코드가 효율적이라고 주장하는 것은 아님!)
    local(%digits) = (%digits, T => 10, E => 11);
    parse_num();
}
아마도 여러분은 local이 실제로 대부분의 사람들이 생각하는 지역변수와는 다르기 때문에 my를 사용하기를 원할것이다. my에 관한 부분을 참고하라.

localtime
localtime EXPR
이 함수는 time의 반환값을 지역 시간대에 맞게 고쳐진 시간과 함께 9개의 인수를 리스트로 변환한다. 이 함수는 대개 다음과 같이 사용된다.
($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);
모든 리스트 요소는 수치값을 가지며 struct tm 구조의 요소이다.(이 표현은 C 프로그래밍 언어에서 사용되는 것이다. 따라서 사용자는 걱정할 필요 없다.) 특히, $mon는 0..11의 값을, $wday는 0..6의 값을 그리고 $year는 현재의 값에서 1900을 뺀 값을 가진다는 것을 의미한다.(사용자는 항상 월명과 요일명을 포함한 0을 근간으로 한 배열의 서브스크립트로 그것들을 사용하므로, 사용자는 어느 것이 0을 근간으로 만들어졌는지를 기억할 수 있다.)
만약 EXPR이 생략되면, 함수는 localtime(time)의 형태로 처리한다. 예를 들면, 현재 무슨 요일인지 알기 위해서는:
$thisday = (Sun,Mon,Tue,Wed,Thu,Fri,Sat)[(localtime)[6]];
Perl 라이브러리 모듈 Time::Local 은 반대로 변환하는데 사용할 수 있는 timelocal() 서브루틴을 포함하고 있다.
스칼라 구문에서 localtimectime(3)을 반환한다-현지 시각의 형태인 문자열처럼. 예를 들면 명령어 date는 다음의 형태로 에뮬에이션 할 수 있다:
perl -e ‘print scalar localtime’
시간을 좀더 세련되게 모양을 갖추어 출력하고자 하면 7장의 POSIX::strftime() 도 참고하라.

log
log EXPR
이 함수는 EXPR의 로그값(베이스는 e)을 반환한다. 만약 EXPR이 명시되지 않으면 그 함수는 변수 $_의 로그값을 반환한다.

lstat
lstat EXPR
이 함수는 stat 함수와 같은 기능을 하나, 만약 파일 이름의 맨 뒤 요소가 심볼릭 링크이면, 심볼릭 링크가 가르키는 파일 대신에 심볼릭 링크를 인자로 사용한다.(만약 심볼릭 링크가 사용자의 컴퓨터에서 지원되지 않으면, 대신에 stat 함수가 실행된다.)

map
map BLOCK LIST
map EXPR, LIST
이 함수는 BLOCK 혹은 EXPRLIST의 각 요소(지역적으로 변수 $_를 각 요소에 설정)에 대해 처리하며. 각 처리 결과로 구성되는 리스트 값을 반환한다. 이 함수는 리스트 구문 형태로 BLOCK 혹은 EXPR를 처리하며, 그 결과 LIST의 각 요소는 반환값으로 0, 1, 혹은 여러 개의 요소를 반환한다. 이러한 것들은 모두 하나의 리스트로 합쳐진다. 예를 들면:
@words = map { split ‘ ‘ } @lines;
는 행으로 구성된 리스트를 분리하여 워드로 구성된 리스트를 만든다. 종종 입력값과 출력값간의 1:1 매핑이 존재한다:
@chars = map chr, @nums;
앞의 예는 숫자로 구성된 리스트를 해당하는 문자로 변환한다. 그리고, 다음은 1:2 매핑의 예이다:
%hash = map { genkey($_), $_ } @array;
그것은 다음 코드를 작성하는 단지 재미있는 기능적인 방법의 하나이다:
%hash = ();
foreach $_ (@array) {
    $hash{genkey($_)} = $_;
}
grep도 참고하라. map은 연속되는 EXPR의 처리 결과로 구성되는 리스트를 반환하며, 반면에 grep은 처리된 EXPR이 참인 LIST의 값으로 구성된 리스트를 반환한다는 점에서 점에서 mapgrep과 다르다.

mkdir
mkdir FILENAME, MODE
이 함수는 FILENAME로 명시된 디렉토리를 생성하며, 이 때 디렉토리의 퍼미션은 절대모드 값을 가진다(현재의 umask 값으로 변경가능). 만약 그 함수 실행이 성공적이면 1을 반환값으로, 그렇지 않으면 0을 반환값으로 주며 $!변수를 설정한다(errno의 값을 설정). 만약 mkdir(2)이 C 라이브러리에 존재하면 Perl은 mkdir(1)을 호출하여 에뮬레이션 한다.
만약 사용자가 시스템상에서 많은 디렉토리를 생성하고자 하면, 생성하고자 하는 디렉토리 리스트와 함께 mkdir을 호출하는 것이 훨씬 효율적이며, 그렇게 함으로써 많은 부(sub) 프로세스를 생성하지 않을 수 있다.

msgctl
msgctl ID, CMD, ARG
이 함수는 msgctl(2) 시스템 호출을 이용한다. 더 상세한 내용은 msgctl(2)을 참고하라. 만약 CMD가 &IPC_STAT이며, ARG는 반드시 반환되는 msqid_ds 구조를 가지는 변수이어야 한다. 반환값은 ioctl과 같이 동작한다: 오류시에는 정의되지 않은 값을, 0인 경우에는 “0 이지만 참” 혹은 그 밖의 경우에는 실제 값을 반환한다. 오류가 발생한 경우 이 함수는 오류 코드를 변수 $!에 저장한다. 호출하기 전에 사용자는 다음과 같이 해야 한다:
require “ipc.ph”;
require “msg.ph”;
이 함수는 시스템 V IPC를 지원하는 머신에만 존재하며, 소켓을 지원하는 머신보다는 그 수가 훨씬 적다.

msgget
msgget KEY, FLAGS
이 함수는 시스템 V IPC의 시스템 호출을 이용한다. 더 상세한 내용은 msgget(2)을 참고하라. 이 함수는 메시지 큐 ID를 반환하거나, 오류가 발생하면 정의되지 않은 값을 반환한다. 오류가 발생한 경우 이 함수는 오류 코드를 변수 $!에 저장한다. 호출하기 전에 사용자는 다음과 같이 해야 한다:
require “ipc.ph”;
require “msg.ph”;
이 함수는 시스템 V IPC를 지원하는 머신에만 존재한다.

msgrcv
msgrcv ID, VAR, SIZE, TYPE, FLAGS
이 함수는 시스템 호출인 msgrcv(2)를 이용하여 메시지 큐 ID로부터 최대 크기인 SIZE 값의 메시지를 받아 변수 VAR에 저장한다. 더 상세한 내용은 msgrcv(2)를 참고하라. 메시지를 받으면 그 메시지 종류는 VAR의 첫번째가 되며 그리고 VAR의 최대 길이는 SIZETYPE의 크기를 더한 것이 된다. 이 함수는 성공시에 참값을, 오류가 발생한 경우에는 거짓값을 반환한다. 오류가 발생한 경우 이 함수는 오류 코드를 변수 $!에 저장한다. 호출하기 전에 사용자는 다음과 같이 해야 한다:
require “ipc.ph”;
require “msg.ph”;
이 함수는 시스템 V IPC를 지원하는 머신에만 존재한다.

msgsnd
msgsnd ID, MSG, FLAGS
이 함수는 시스템 호출인 msgsnd(2)를 이용하여, MSG를 메시지 큐 ID에 보낸다. 더 상세한 것은 msgsnd(2)를 참고하라. MSG는 롱(long int) 메시지 형태로 시작해야 한다. 사용자는 다음과 같이 메시지를 생성할 수 있다:
$msg = pack “L a*”, $type, $text_of_message;
이 함수는 성공시에 참값을, 오류가 발생한 경우에는 거짓값을 반환한다. 오류가 발생한 경우 이 함수는 오류 코드를 변수 $!에 저장한다. 호출하기 전에 사용자는 다음과 같이 해야 한다:
require “ipc.ph”;
require “msg.ph”;
이 함수는 시스템 V IPC를 지원하는 머신에만 존재한다.

my
my EXPR
이 연산자는 블록, 서브루틴, eval, 또는 블록을 감싸고 있는 가장 안쪽에서만 존재하는 한 개 이상의 지역 변수를 선언한다. 만약 1개 이상의 변수를 선언하면 리스트는 반드시 괄호안에 있어야 한다. 왜냐하면 연산자는 콤마보다 바인딩 우선순위가 높기 때문이다. 단순한 스칼라 혹은 완전한 배열과 해쉬는 이러한 식으로 선언될 수 있다. 변수 이름은 검증된 패키지가 아닐 수도 있다, 왜냐하면 패키지 변수는 모두 전역변수이며, 지역 변수는 패키지와 관련되어 있지 않다. local과 달리 그 영역내에서 보여지는 같은 이름의 다른 변수를 감추는 것 외에는, 이 연산자는 전역 변수와는 전혀 관련이 없다. (그러나, 전역 변수는 항상 자신의 패키지를 통해 접근이 가능하다-자격을 갖춘 형식) 지역 변수는 그 선언 이후 변수가 사용되는 문이 존재할 때까지는 볼 수가 없다. 그런 지역 변수의 영역내에서 호출된 서브루틴은 그 변수 영역내에서 문자로 서브루틴이 선언되지 않으면 지역 변수를 볼 수 없다. 이것에 대한 기술적 용어는 “문법적 영역”이라고 하며 종종 그 변수를 “렉시컬 변수(lexical variables)”라 부른다. C언어의 세계에서는 이들은 “자동(auto)” 변수라고 불린다. 왜냐하면 이들 변수는 해당하는 영역으로 들어감과 빠져나옴에 따라서 자동적으로 메모리상에 할당되고 제거되기 때문이다.
원한다면 EXPR에 특정값을 할당하여 사용자의 변수를 초기화할 수 있다.(만약 초기화하지 않으면 모든 스칼라는 정의되지 않은 값으로 초기화 되며 모든 배열과 해쉬는 빈값을 가지게 된다.) 일반적인 할당문처럼 사용자가 왼쪽에 위치한 변수 주위에 괄호를 사용하고(혹은 그 변수가 배열 혹은 해쉬이면), 오른쪽에 위치한 식은 리스트 형태로 해석된다. 그렇지 않으면 오른쪽에 위치한 그 식은 스칼라 형태로 해석된다. 사용자는 리스트 할당과 함께 사용자의 정식 서브루틴 인자를 다음과 같이 명시할 수 있다:
my ($friends, $romans, $countrymen) = @_;
다음과 같이 리스트 할당을 표시하는 괄호를 빠뜨리지 않도록 주의하라:
my $country = @_; # 옳은가 , 틀렸는가 ?
이것은 배열의 길이(즉 서브루틴의 인자의 수)를 변수에다 할당한다. 왜냐하면 그 배열은 스칼라 형태로 해석되기 때문이다. 사용자가 shift 연산자를 사용하는 한, 사용자는 정식 인자를 위해 스칼라 할당을 유익하게 이용할 수 있다. 왜냐하면, 객체 방식은 첫번째 인자로 전달되는 객체이며, 그러한 많은 방식의 서브루틴은 다음과 같이 시작된다:
sub simple_as {
my $self = shift; # 스칼라 할당
my ($a,$b,$c) = @_; # 리스트 할당

}

new
new CLASSNAME LIST
new CLASSNAME
내장된 new 함수는 존재하지 않는다. 이 함수는 사용자로 하여금 CLASSNAME 종류의 객체를 구성(construct)할 수 있도록, CLASSNAME 모듈로 정의된(혹은 승계된) 일반적인 구성자 방식(서브루틴)의 하나이다. 대부분의 구성자는 단지 관례상 “new”라고 명명되나, 이것은 C++ 프로그래머로 하여금 무엇이 진행되고 있는지를 알고 있다고 착각하게 한다.

next
next LABEL
next
이 명령어는 C 언어의 continue 문과 같다. 이것은 LABEL로 지정된 루프를 실행한다:
LINE: while (<STDIN>) {
    next LINE if /^#/; # 주석을 제거
    …
}
만약 앞의 예제에서 continue 블록이 존재하면 그 다음 next 문이 존재하는 위치로 실행점이 옮겨가서 시작된다. LABEL이 존재하지 않으면 그 명령어는 가장 안쪽의 포괄하는 루프를 참조하게 된다.

no
no Module LIST
no 문의 반대 기능을 하는 use 연산자를 참고하라.

oct
oct EXPR
oct 함수는 8진수 형식의 EXPR을 10진수로 변환한다.(만약 EXPR이 0x로 시작하면 16진수로 해석한다) 다음은 표준 표시형태로 10진수, 8진수, 16진수를 처리하는 예이다:
$val = oct $val if $val =~ /^0/;
만약 EXPR이 명시되지 않으면 그 함수는 $_를 해석한다. 8진수 숫자에 대한 역 함수를 사용하고자 하면:
$oct_string = sprintf “%lo”, $number;

open
open FILEHANDLE, EXPR
open FILEHANDLE
이 함수는 EXPR의 파일이름을 가지는 파일을 열고, 그 것을 FILEHANDLE과 연관지운다. 만약 EXPR가 명시되어 있지 않으면, FILEHANDLE과 같은 이름의 스칼라 변수가 반드시 파일이름을 가져야 한다.(그리고 사용자는 “|| die”보다 “or die”를 문장의 뒤에 사용할 때 주의해야 한다. 왜냐하면 ||의 실행 순위가 open과 같은 리스트 연산자보다 높기 때문이다.) FILEHANDLE은 직접 명시된 파일핸들 이름이거나 혹은 파일핸들로 사용될 식의 값일 수도 있다. 후자를 간접 파일핸들이라 부른다. 만약 사용자가 간접 파일핸들로 정의 되지않은 값을 사용하면, Perl이 자동적으로 그것을 채우지는 않는다. 사용자는 반드시 그 식이 단일의 값 즉 실제 파일핸들 이름이거나 객체 지향적인 입출력 패키지로부터 나온 파일핸들 객체에 반환되도록 해야한다.(파일핸들 객체는 구성자 호출을 통한 객체를 생성하므로 유니크(unique)하다. 이 부분의 뒤쪽에 있는 예제를 참고하라.)
파일핸들이 결정된 후, 파일이름 문자열이 처리된다. 즉 먼저 파일이름인 문자열에 존재하는 공백문자가 제거된다. 그리고 나서 파일이 어떻게 열리는지를 명시하는 문자를 찾기 위해 문자열은 처리된다.(놀라운 우연이지만 이들 문자들은 본(Bourne) 쉘에다 입출력 재지정(redirection)을 나타내기 위해 사용하는 문자와 같은 형태이다.) 만약 파일이름 앞에 <이 존재하면 그 파일은 입력용 데이터를 얻기위해 열려진다. 만약 파일이름 앞에 <이 존재하면 그 파일은 데이터 출력을 위해 열려진다. 만약 파일이름 앞에 >>이 존재하면 그 파일은 데이터가 추가되는 형태로 열려진다.(사용자는 파일에 읽고, 쓰기를 모두하고자 하면 > 혹은 < 앞에 +를 사용하면 된다.) 파일이름 앞에 |가 존재하면 파일이름은 출력 결과가 파이프를 통해 전달되는 명령어로 해석된다. 그리고, 파일이름 뒤에 |가 존재하면 그 파일이름은 입력을 사용자쪽으로 파이프시키는 명령어로 해석된다. 비록 IPC::Open2와 IPC::Open3이 거의 동일한 기능을 사용자에게 제공한다 하더라도 사용자는 입출력을 파이프하는 open 명령어를 가질 수는 없다. 6장의 “양방향 통신”을 참고하라
쉘 메타문자를 포함한 파이프 명령어는 실행되기 위해서는 /bin/sh에 전달된다; 그렇지 않은 경우에는 Perl이 직접 실행한다. 파일이름 “-“는 STDIN를 의미하며 “>-“는 STDOUT를 의미한다. open의 실행이 성공인 경우 0이 아닌 값을, 실패인 경우는 정의되지 않은 값을 반환한다. 만약 파이프가 open과 함께 사용되면 서브 프로세스의 프로세스 ID를 반환한다.
만약 사용자가 텍스트 파일과 이진 파일을 구별하는 시스템에서 Perl을 실행해야 하는 불행한 경우라면(현대의 OS는 개의치 않는다), 사용자는 이것을 처리하기 위한 팁(tips)으로 binmode를 충분히 검토해야 한다. binmode를 필요로 하는 시스템과 그렇지 않은 시스템간의 핵심적인 차이점은 텍스트 파일 포맷이다. 단일 문자를 이용하여 행을 구분하며, C 언어에서 그 단일 문자를 ‘\n’로 변환하는 유닉스나 Plan 9와 같은 시스템에서는 binmode가 필요하지 않다. 나머지는 그것이 필요하다. 다음은 파일이름과 같은 이름을 가진 변수간의 관련성을 보여주는 코드의 예다:
$ARTICLE = “/usr/spool/news/comp/lang/perl/misc/38245”;
open ARTICLE or die “Can’t find article $ARTICLE: $!\n”;
while (<ARTICLE>) {…
다음과 같이 파일에다 데이터를 더 추가할 수 있다:
open LOG, ‘>>/usr/spool/news/twitlog’; # `log’는 예약되어 있는 키워드다.
프로세스의 데이터를 파이프를 통해 파일에다 추가한다:
open ARTICLE, “caesar <$article |”; # rot13 알고리듬으로 article을 해독함
여기서 <은 EXPR의 첫번째 문자가 아니므로 Perl이 입력을 위해 파일을 읽어들이는 것을 뜻하지 않는다. 오히려 |이 caesar <$article의 입력을 파이프하는 것을 뜻한다.(프로그램 caesar가 $article을 기본입력으로 받는다고 할 경우) <는 서브쉘에 의해 Perl이 파이프를 시작한다는 의미로 해석된다. 왜냐하면 <는 쉘 메타문자이기 때문이다. 혹은 사용자의 데이터를 파이프를 이용해 프로세스에 입력할 수 있다.
open EXTRACT, “|sort >/tmp/Tmp$$”; # $$은 프로그램의 프로세스 숫자
다음 예제에서는 간접 파일핸들을 이용하여 재귀적인 열기를 하는 방법을 보여준다. 파일들은 파일핸들인 fh01, fh02, fh03 등과 같은 값을 통해 열려진다. $input은 지역 변수이므로 재귀(recursion)를 통해 보존되며, 반환값을 주기 전에 사용자로 하여금 정확한 파일을 닫을 수 있도록 해준다.
# Process argument list of files along with any includes.
foreach $file (@ARGV) {
    process($file, ‘fh00’);
}
sub process {
    local($filename, $input) = @_;
    $input++; # this is a string increment
    unless (open $input, $filename) {
        print STDERR “Can’t open $filename: $!\n”;
        return;
    }
    while (<$input>) { # note the use of indirection
        if (/^#include “(.*)”/) {
            process($1, $input);
            next;
        }
        … # whatever
    }
    close $input;
}
본(Bourne) 쉘의 관례를 따라, 사용자는 EXPR 앞에 >&를 명시하며 >&를 제외한 문자열은 파일핸들(혹은 숫자인 경우 파일 기술자) 이름으로 해석되며, 파일핸들은 복사되어 열리게 된다. 사용자는 >, >>, <, +>, +>>, 그리고 +<.뒤에 &를 사용한다. 사용자가 명시하는 모드는 원래의 파일핸들의 모드와 일치해야 한다. 다음은 STDOUT와 STDERR를 저장, 재지정(redirect) 그리고 복원하는 스크립트 예이다.
#!/usr/bin/perl
open SAVEOUT, “>&STDOUT”;
open SAVEERR, “>&STDERR”;
open STDOUT, “>foo.out” or die “Can’t redirect stdout”;
open STDERR, “>&STDOUT” or die “Can’t dup stdout”;
select STDERR; $| = 1; # make unbuffered
select STDOUT; $| = 1; # make unbuffered
print STDOUT “stdout 1\n”; # this propagates to
print STDERR “stderr 1\n”; # subprocesses too
close STDOUT;
close STDERR;
open STDOUT, “>&SAVEOUT”;
open STDERR, “>&SAVEERR”;
print STDOUT “stdout 2\n”;
print STDERR “stderr 2\n”;
만약 사용자가 숫자를 값으로 가지는 N에 대하여 <&=N를 명시하면, Perl은 그 파일 기술자인 C언어의 fdopen(3)과 같은 기능을 한다: 이것은 앞서 설명한 dup 형태보다 좀 부족한 파일 기술자이다.(한편, 그것은 더 위험하다. 왜냐하면 2개의 파일핸들은 같은 파일 기술자를 공유하고 있을 수도 있으며, 하나의 파일핸들에 대해 close 문을 사용하면 다른 파일핸들을 너무 일찍이 닫을 수 있다.) 예를 들면:
open FILEHANDLE, “<&=$fd”;
만약 사용자가 명령어 “-“(즉 |- 나 -| 이면)와 파이프를 열면, 암묵적으로 fork 문이 동작하며 open의 반환값은 모(parent) 프로세스가 생성한 자(child) pid가 되거나 그 자(child) 프로세스내에서 0이 된다.(open이 성공적으로 수행되었는지를 알기 위해서는 모(parent)나 자(child) 프로세스에서 defined($pid)를 사용하라) 파일핸들은 모 프로세스에 대해서는 정상적으로 동작하나, 그 파일핸들의 입출력은 자 프로세스의 STDOUT나 STDIN과 파이프가 연결된다. 자 프로세스에서는 파일핸들이 열리지 않는다 – 입출력 는 새로운 STDIN이나 STDOUT과 이루어진다, 사용자가 setuid를 실행하고 메타문자를 찾기위해 쉘 명령어를 검색하지 않고자 하는 경우처럼, 파이프 명령어가 어떻게 실행되는 가를 제어하고자 할 때 대개 이것은 일반적인 파이프된(piped) open 형태로 사용된다. 다음의 쌍으로 표기된 예제는 동일하다:
open FOO, “|tr ‘[a-z]’ ‘[A-Z]'”;
open FOO, “|-” or exec ‘tr’, ‘[a-z]’, ‘[A-Z]’;
open FOO, “cat -n file|”;
open FOO, “-|” or exec ‘cat’, ‘-n’, ‘file’;
파이프된(piped) 파일핸들을 명시적으로 닫으면 모(parent) 프로세서는 자 프로세스가 종료하기를 기다리며, $?의 상태값을 반환한다. fork를 수행하는 모든 연산에 대해서, 플러쉬(flush)되지 않은 버퍼는 그 두개의 프로세스에서도 플러쉬되지 않는다. 그것은 사용자가 중복된 출력을 피하기 위해 1개 이상의 파일핸들에 대하여 $|를 설정할 필요가 있다는 것을 의미한다.(그리고 나서 그들을 플러쉬(flush)하기 위해 출력한다). 파일핸들 STDIN, STDOUT, 그리고 STDERR는 exec 문 뒤에도 열려있다. 다른 파일핸들은 그렇지 않다.(그러나, fcntl 함수를 지원하는 시스템에서 사용자는 파일핸들용으로 close-on-exec 플래그를 변경해야 한다. 이번 장 앞부분의 fcntl를 참고하라. 또한 특수 변수 $^F를 참고하라.) 7장에서 설명되는 파일핸들(FileHandle) 모듈의 구성자(constructor)를 사용하면, 사용자는 익명 파일핸들을 생성할 수 있다. 그 파일핸들은 그들에 대한 레퍼런스를 저장할 변수들의 영역을 포함하며, 그리고 때와 방법을 가리지 않고 사용자가 그 영역을 벗어나면 자동적으로 닫는다.
use FileHandle;
sub read_myfile_munged {
    my $ALL = shift;
    my $handle = new FileHandle;
    open $handle, “myfile” or die “myfile: $!”;
    $first = <$handle> or return (); # 여기서 자동적으로 닫힌다
    mung $first or die “mung failed”; # 또는 여기
    return $first, <$handle> if $ALL; # 또는 여기.
    $first; # 또는 여기.
}
파일이름에 이상한 문자가 들어가 있는 경우에 파일을 열고자 하면, 다음과 같이 파일이름내의 공백문자를 보존해야 한다:
$file =~ s#^\es#./$&#;
open FOO, “< $file\e0”;
그러나, 실제로 스크립트에 위와 같이 사용하는 사람을 한번도 본적이 없다
만약 사용자가 실제 C 언어의 open(2)을 사용하기를 원한다면 sysopen 함수를 사용해야 한다. 이것은 바로 파일이름의 잘못된 해석을 방지할 수 있는 다른 방법이기도 하다. 예를 들면:
use FileHandle;
sysopen HANDLE, $path, O_RDWR|O_CREAT|O_EXCL, 0700
or die “sysopen $path: $!”;
HANDLE->autoflush(1);
HANDLE->print(“stuff $$\n”);
seek HANDLE, 0, 0;
print “File contains: “, <HANDLE>;
읽기와 쓰기에 관한 상세한 내용은 seek을 참고하라.

opendir
opendir DIRHANDLE, EXPR
이 함수는 readdir, telldir, seekdir, rewinddir, 그리고 closedir가 처리하고자 하는 EXPR라는 이름을 가진 디렉토리를 연다. 이 함수는 성공적인 경우 참값을 반환한다. 디렉토리 핸들은 파일핸들과 구별되는 고유의 이름영역을 가진다.

ord
ord EXPR
이 함수는 EXPR의 첫번째 문자를 ASCII 값으로로 변환한다. 만약 EXPR이 생략되면 변수 $_를 인자로 사용한다. 반환값은 항상 비부호화(unsigned) 값이다. 만약 사용자가 부호화(signed)된 값을 원하면 unpack(‘c’, EXPR)을 사용하면 된다. 만약 문자열의 모든 문자를 숫자로 변환하기를 원하면 unpack(‘C*’, EXPR)를 사용하면 된다.

pack
pack TEMPLATE, LIST
이 함수는 값들로 구성된 리스트를 받아서 이진 구조로 변환시키며, 구조를 내포한 문자열을 반환한다. TEMPLATE은 다음과 같이 값의 순서 및 종류를 정의하는 문자열의 시퀀스(sequence)이다.

글자

의미

a

문자로 채워진 ASCII 문자열

A

스페이스로 채워진 ASCII 문자열

b

비트 문자열, 낮을수록 먼저

B

비트 스트링, 높을수록 먼저

c

signed 문자

C

unsigned 문자

d

배정도 부동소수점(실수)

f

단일정도 부동소수점(실수)

h

16진수 문자열, 낮은 숫자 먼저

H

16진수 문자열, 높은 숫자 먼저

I  

signed 정수

I

unsigned 정수

l

signed long 정수

L

unsigned long 정수

n

네트웍에서의 short 정수

N

네트웍에서의 long 정수

p

스트링에 대한 포인터

P

구조에 대한 포인터(고정길이 스트링)

s

signed short 정수

S

unsigned short 정수

v

VAX에서 short 정수

V

VAX에서 long 정수

u

문자열을 uuencode형식으로 변환

x

바이트

X

바이트 뒤로

@

절대 위치까지 널로 채움

각 글자의 뒤에는 반복 횟수가 따라올 수 있다. 글자와 반복횟수가 함께 필드에 대한 지정자 역할을 한다. 필드 지정자는 공백으로 분리되며 공백은 무시된다. “a”와 “A”를 제외한 모든 유형을 이용해 함수 packLIST의 많은 값들을 처리할 수 있다. 반복 횟수를 상징하는 “*”는 아무리 많은 항목이 있어도 사용한다는 의미이다. “a”와 “A” 유형은 단지 1개의 값을 처리하나, 그것을 길이 계산의 문자열로 변환하여, 필요에 따라 널이나 공백으로 채울 수 있다.(unpacking하는 경우 “A”는 맨뒤의 공백(spaces)이나 널을 제거하나, “a”는 그렇지 않다.) 실수형(실수와 더블(double))은 그 기계 자체의 포맷으로 표현되며 이는 실수 포맷의 다양성과 표준 네트웍 표기법의 부재, 상호 교환 장치의 부재등이 그 이유이다. 이것은 한 기계에서 pack으로 작성된 것이 다른 기계에서는 판독이 불가능할 수 있다는 것을 의미한다. 비록 두 기계가 IEEE 실수 연산을 한다 하더라도(메모리 표기의 엔디안-니스(endian-ness)가 IEEE규격의 한 부분이 아닌것처럼). 또한 Perl은 모든 수치 계산에 내부적으로 더블(doubles)을 사용하며, 더블(doubles)을 실수로 그리고 다시 더블(doubles)로 변환하게 되면 정도(precision)을 잃게 된다; 즉 unpack(“f”, pack(“f”,$num)) 연산의 결과는 대개 $num와 같지 않다. 다음 첫번째 한쌍의 예제는 수치값을 바이트로 pack하는 것이다:
$out = pack “cccc”, 65, 66, 67, 68; # $out eq “ABCD”
$out = pack “c4”, 65, 66, 67, 68; # 위와 같음
다음은 몇 개의 널을 삽입한 앞의 예제와 비슷한 것이다.
$out = pack “ccxxcc”, 65, 66, 67, 68; # $out eq “ABCD”
short 정수를 팩(pack) 하더라도 이식성을 보장하지는 못한다:
$out = pack “s2”, 1, 2; # “\1\2”
# “\1\2”
2진수 및 16진수에 대한 pack 연산의 경우, 카운트(count)는 생성된 바이트의 수가 아니라 비트 수 혹은 니블(nybbles, 4비트)을 의미한다:
$out = pack “B32”, “01010000011001010111001001101100”;
$out = pack “H8”, “5065726c”; # 둘다 “Perl”로 변환
“a” 필드상의 길이는 단지 1개의 문자열에 대해서만 적용된다:
$out = pack “a4”, “abcd”, “x”, “y”, “z”; # “abcd”
이런 한계를 극복하기 위해서는 여러 개의 지정자를 사용한다:
$out = pack “aaaa”, “abcd”, “x”, “y”, “z”; # “axyz”
$out = pack “a” x 4, “abcd”, “x”, “y”, “z”; # “axyz”
“a” 포맷은 널을 채운다.
$out = pack “a14”, “abcdefg”; # “abcdefg\e0\e0\e0\e0\e0\e0\e0”
이 템플릿은 C언어 구조체 tm의 레코드를 pack 연산한다(적어도 특정 시스템에서는):
$out = pack “i9pl”, gmtime, $tz, $toff;
일반적으로 같은 템플릿은 함수 unpack에도 똑같이 사용할 수 있다. 만약 사용자가 구분자를 사용하여 가변 길이 필드를 결합하고자 하면, 함수 join 사용한다.
앞의 모든 예제들이 문자열을 템플릿으로 사용하더라도, 디스크 파일에서 사용자의 템플릿을 가져오지 못할 이유는 없다는 점을 유의한다. 사실 이 함수를 이용하여 관계형 데이터 베이스 시스템도 구축할 수 있다.

package
package NAMESPACE
이것은 실제로는 함수가 아니라 선언문이다. 즉 이것은 블록, 서브루틴, eval 혹은 파일을 감싸는 가장 안쪽의 나머지 부분이 명시된 이름 공간에 속한다는 것을 선언하는 것이다.(따라서 package 선언문의 영역은 local이나 my선언문과 같다) 지정되지 않은 전역 식별자에 대한 일련의 모든 레퍼런스는 선언된 패키지의 심볼 테이블을 검색하여 결정된다. package 선언문은 local을 사용한 변수를 포함하여 전역 변수에 적용되지만 my를 이용해서 선언한 지역 변수에는 적용되지 않는다.
대개 사용자는 package 선언을 requireuse를 이용하여 파일의 앞부분에 한다. 그러나, 문법에 맞는 위치라면 사용자는 그 package 선언문을 어디에든 둘 수 있다. 클래스나 모듈 파일을 정의할 때 혼돈을 피하기 위해 파일과 같은 이름으로 패키지를 명명하는 것이 관례이다.(그리고 패키지 이름의 첫글자를 대문자로 사용하는 것도 관례이다. 왜냐하면 소문자로 명기된 모듈은 관례상 프라그마로 해석되기 때문이다.)
사용자는 어느 위치에서건 주어진 패키지로 스위칭(실행위치를 이동)할 수 있다; 그것은 단지 블록의 나머지 부분에 대하여 컴파일러가 사용하는 심볼 테이블에 대해서만 영향을 미친다.(만약 같은 수준의 다른 package 선언문을 보면 새 선언문이 그 이전의 선언문을 무시하게 된다.) 사용자 주 프로그램은 package 주 선언문으로 시작한다고 가정한다.
사용자는 패키지 이름과 이중 콜론: $Package::Variable으로 식별자를 지정함으로써 다른 패키지의 변수와 파일핸들을 창조할 수 있다. 만약 패키지 이름이 널이면 주 패키지를 사용하는 것으로 전제한다. 다시 말해 $::sail은 $main::sail과 같다는 것이다.
패키지의 심볼 테이블은 이중 콜론으로 끝나는 이름을 가진 해쉬에 저장된다. 예를 들어, 주 패키지의 심볼 테이블 이름은 %main::이다. 따라서, 패키지 심볼 *main::sail은 $main::{“sail”}로 참조가 가능하다.
패키지, 모듈 그리고 클래스에 관한 정보를 더 알고자 하면 5장의 “패키지”를 참고하라. 다른 영역(scoping) 문제에 관한 것은 3장의 my를 참고하라.

pipe
pipe READHANDLE, WRITEHANDLE
상응하는 시스템 호출처럼 이 함수는 한쌍의 연결된 파이프를 연다 – pipe(2)를 참고하라. 이 호출은 거의 항상 fork 보다 앞서 사용되며, 그리고 나서 파이프의 리더(reader)는 WRITEHANDLE을 받드시 닫아야 한다. 그리고, 라이터(writer)는 READHANDLE을 닫는다.(그렇지 않으면 라이터(writer)는 그것을 닫을 때, 파이프는 리더(reader)에게 EOF를 나타내지 않는다.)
만약 파이프된 프로세스로 이루어진 루프를 생성하면 사용자가 주의하지 않으면 교착상태(deadlock, 여러 프로세서들이 자원(resource)을 공유하여 수행할 때 둘 이상의 프로세스가 서로 다른 프로세스들이 차지하고 있는 자원들을 요구하면서 무한정 기다리게 되어 결국 그 프로세스들이 전혀 수행되지 못하는 상태 – 역자 주)가 발생할 수 있음을 유의해야 한다. 게다가 Perl의 파이프는 표준 입출력 버퍼링을 사용하며, 그 결과 응용 프로그램에 따라 사용자는 각 출력 명령어 다음에 플러쉬(flushing)하기 위해 자신의 WRITEHANDLE에 $|를 설정할 필요가 있는 경우도 있다. select(출력 파일핸들)를 참고하라. 또한 6장의 “파이프” 부분을 참고하라.

pop
pop ARRAY
pop
이 함수는 배열을 스택처럼 사용한다. 즉 배열의 맨 마지막 값을 반환하며, 배열의 마지막 인덱스에서 1을 뺀다.
만약 ARRAY를 사용하지 않으면 @ARGV(주 프로그램에서)나 @_(서브루틴에서)에서 값을 꺼낸다.
이는 다음과 같은 결과를 얻게 된다.
$tmp = $ARRAY[$#ARRAY–];
또는:
$tmp = splice @ARRAY, -1;
배열에 요소가 전혀 없으면 pop은 정의되지 않은 임의의 값을 반환한다.
pushshift도 참고하기 바란다. 만약 2개 이상의 요소를 꺼내고자 하는 경우에는 splice를 사용한다.
pop의 첫번째 인자는 리스트가 아닌 받드시 배열이어야 한다. 만약 리스트의 맨 마지막 요소를 원한다면 다음과 같이 하면 된다.
(something_returning_a_list)[-1]

pos
pos SCALAR
함수는 SCALAR에 대해 마지막 m//g 검색이 이루어진 후의 나머지 부분에 대한 위치를 반환한다. 그것은 일치되는 마지막 것 다음의 글자 옵셋(offset)값을 반환한다. (다시 말해, length($`)+length($&)의 값과 같다) 이것은 그 문자열에 대한 다음 m//g 검색이 시작하는 옵셋(offset)값이다. 문자열의 시작부분의 옵셋(offset) 값은 0인 것을 기억하라. 예를 들면:
$grafitto = “fee fie foe foo”;
while ($grafitto =~ m/e/g) {
    print pos $grafitto, “\n”;
}
는 “e” 뒤에 위치하는 각 글자의 옵셋(offset)값인 2, 3, 7 그리고 11을 출력한다. pos 함수에 그 다음 m//g에게 시작할 위치를 알려주는 값이 할당된다.
$grafitto = “fee fie foe foo”;
pos $grafitto = 4; # fee를 무시하고 fie에서 시작함
while ($grafitto =~ m/e/g) {
    print pos $grafitto, “\n”;
}
이것은 7과 11만을 출력한다. 정규식 \G을 사용하면 검색하는 문자열에 대하여 pos 문에 의해 현재 지정되는 위치에만 일치한다.

print
print FILEHANDLE LIST
print LIST
print
이 함수는 문자열이나 콤마로 구분되는 문자열 리스트를 출력한다. 이 때 성공적이면 1을 그렇지 않으면 0을 반환한다. FILEHANDLE은 스칼라 변수 이름이며, 그 변수는 실제 파일핸들 이름이나 객체지향적인 파일핸들 패키지중 하나에서 나온 파일핸들 객체의 레퍼런스를 가진다.
FILEHANDLE은 여러 종류의 값을 반환하는 블록일 수도 있다.
print { $OK ? “STDOUT” : “STDERR” } “stuff\n”;
print { $iohandle[$i] } “stuff\n”;
만약 FILEHANDLE이 변수이고 그 다음 토큰이 항목이면, 인자 사이에 +를 사용하지 않거나 인자를 괄호안에 표기하지 않으면 FILEHANDLE은 하나의 연산자로 잘못 해석되어 실행될 수 있음에 유의해야 한다.
print $a – 2; # 디폴트 파일핸들(대개 STDOUT)에 $a – 2를 출력.
print $a (- 2); # $a에 지정된 파일핸들에 -2를 출력
print $a -2; # 위와 같음 (이상한 파싱(parsing) 규칙 :-)
만약 FILEHANDLE이 사용되지 않으면, print는 현재 선택된 출력 파일핸들인 STDOUT에 출력한다. STDOUT이 아닌 디폴트 출력용 파일핸들을 설정하려면 select(FILEHANDLE)[7]을 사용한다. 만약 LIST가 생략되면 $_를 출력한다. printLIST를 사용하기 때문에, LIST의 모든 것은 리스트 구문의 형태로 처리되며, 사용자가 호출하는 모든 서브루틴은 리스트 구문에서 처리되는 1개 이상의 자신의 내장 식을 가진다는 것을 유의해야 한다. 따라서, 다음과 같을 때:
print OUT <STDIN>;
그것은 표준입력의 그 다음 행을 출력하지 않고, 표준입력의 나머지 행들과 파일의 끝까지 출력한다. 왜냐하면 <STDIN>이 리스트 구문의 형태로 반환하는 값이기 때문이다. 또한, “함수처럼 보이는 것은 함수이다”라는 규칙을 기억해야 하며, 만약 사용자가 상응하는 오른쪽 괄호가 print의 인자를 제한하는 역할을 하길 원하지 않으면, 완쪽괄호를 사용하여 print 키워드를 표기하지 않도록 주의해야 한다. 모든 인자 주위에 +를 사용하거나, 괄호를 사용하라:
print (1+2)*3, “\n”; # 잘못된 예
print +(1+2)*3, “\n”; # 올바른 예
print ((1+2)*3, “\n”); # 올바른 예

printf
printf FILEHANDLE LIST
printf LIST
이 함수는 정형화된 문자열을 파일핸들에 출력하며, 만약 FILEHANDLE이 사용되지 않으면 현재 선택된 출력용 파일핸들(초기에는 STDOUT가 파일핸들)에 출력된다.
이 것은 C 라이브러리의 printf(3)fprint(3) 함수와 비숫하다. 단지 필드폭을 결정하는 요소가 Perl에서는 지원되지 않는다는 점만 다르다.
Perl의 print문을 이용하여 필드폭을 결정하여 출력하려면 다음과 같이 한다.
print FILEHANDLE sprintf LIST
printsprint 문을 참고하라. sprint 문의 설명시 정형화된 문자열을 지정하기 위한 리스트에 대한 설명도 같이 한다. 간단한 출력을 하고자 할 때 printf 문을 사용하는 오류에 빠지지 말기 바란다. print가 훨씬 효율적이며 오류를 덜 범하기 쉽다.

push
push ARRAY, LIST
이 함수는 ARRAY를 스택으로 사용하며 LIST의 값을 ARRAY에 저장한다.
ARRAY의 길이는 LIST의 길이만큼 증가한다. 이 함수의 변경된 배열의 길이를 반환한다. push는 다음과 같은 결과를 준다.
foreach $value (LIST) {
    $ARRAY[++$#ARRAY] = $value;
}
혹은:
splice @ARRAY, @ARRAY, 0, LIST;
그러나, 위 코드는 그 앞의 코드보다 훨씬 사용자에게나 컴퓨터에게 효율적이다. 만약 shiftpush를 함께 사용하면 시간적으로 효율적인 shift 레지스터나 큐(queue)를 만들수 있다.
for (;;) {
    push @ARRAY, shift @ARRAY;
    …
}
popunshift를 참고하라.
q/STRING/
q/STRING/
qq/STRING/
qx/STRING/
qw/STRING/
일반화된 인용에 대해서는 2장을 참고하라.

quotemeta
quotemeta EXPR
이 함수는 EXPR에서 영숫자가 아닌 문자 앞에 역슬래쉬를 붙인다. 이것은 치환할 수 있는 문장(겹따옴표로 인용된 문자열, `, 패턴)에서 \Q 이스케이프를 구현한 내장합수이다.

rand
rand EXPR
rand
이 함수는 0과 EXPR사이의 임의의 실수를 만든다.(EXPR는 양수이여야 한다.) 만약 EXPR을 생략하면 0과 1사이의 난수를 만든다.(0을 포함하고 1은 포함하지 않음). 또한 srand를 참고하라. 정수값을 얻기 위해서는 다음과 같이 int 함수를 함께 사용한다:
$roll = int(rand 6) + 1; # $roll 는 1과 6사이의 값을 가지는 정수가 된다.

read
read FILEHANDLE, SCALAR, LENGTH, OFFSET
read FILEHANDLE, SCALAR, LENGTH
이 함수는 지정된 FILEHANDLE로 부터 LENGTH 바이트의 데이터를 읽어서 변수 SCALAR에 저장한다. 이 함수는 실제 읽은 데이터의 바이트 수를 반환하며, 파일의 끝인 경우는 0을 반환한다. 오류가 발생한 경우 정의되지 않은 값을 반환한다. 실제 읽은 데이터의 길이에 맞추어 SCALAR의 크기는 조절된다. OFFSET을 이용하면 저장할 바이트의 변수 위치를 조절할 수 있으며, 사용자는 문자열 중간부터 읽을 수가 있다. 만약 파일핸들 FROM에서 TO로 데이터를 복사하려면, 다음과 같이 한다:
while (read FROM, $buf, 16384) {
    print TO $buf;
}
함수 read의 반대는 단순히 print이며, print 문은 사용자가 쓰려고 하는 문자열의 길이를 이미 알고 있으며, 임의의 길이를 가지는 문자열도 쓸 수 있다는 것을 유의해야 한다. Perl의 read 함수는 실제로 표준 입출력 함수 fread(3)에 의해 구현되었다. 따라서, 실제 read(2) 시스템 호출은 입력 버퍼를 채우기 위해 LENGTH 바이트 이상으로 읽어들일 수 있다. 그리고, fread(3)는 버퍼를 채우기 위해 시스템 호출 read(2)를 2번이상 이용할 수 있다. 더 확실한 제어를 하기 위해서는 sysread를 사용하여 실제 시스템 호출을 지정하라. 만약 사용자가 뛰어난 능력의 소유자가 아니라면, readsysread를 동시에 호출해서는 안된다.

readdir
readdir DIRHANDLE
이 함수는 opendir에 의해 열린 디렉토리 핸들에서 디렉토리 항목을 읽는다. 스칼라 구문에서, 이 함수는 그 다음 디렉토리 항목이 존재하면 그것을 반환하며, 그렇지 않다면 정의되지 않은 값을 반환한다. 리스트 구문에서는 디렉토리의 나머지 모든 항목을 반환하며, 만약 반환할 항목이 존재하지 않으면 널 리스트를 반환한다. 예를 들면:
opendir THISDIR, “.” or die “serious dainbramage: $!”;
@allfiles = readdir THISDIR;
closedir THISDIR;
print “@allfiles\n”;
앞의 코드는 현재 디렉토리의 모든 파일을 한 행으로 프린트한다. 만약 사용자가 “.” 와 “..”항목을 원하지 않으면, 다음을 사용하라.
@allfiles = grep !/^\e.\e.?$/, readdir THISDIR;
그리고, 모든 .* 파일을 피하기 위해서는(ls 프로그램과 같이):
@allfiles = grep !/^\e./, readdir THISDIR;
단지 텍스트 파일을 얻기 위해서는 다음과 같이 한다:
@textfiles = grep -T, readdir THISDIR;
그러나, 마지막 예는 주의해야 한다. 왜냐하면, 만약 그것이 현재 디렉토리가 아니라면 readdir의 결과는 디렉토리 부분들이 뒤를 이은 연결된 형태를 가져야 한다. 다음과 같이:
opendir THATDIR, $thatdir;
@text_of_thatdir = grep -T, map “$thatdir/$_”, readdir THATDIR;
closedir THATDIR;

readlink
readlink EXPR
이 함수는 심볼릭 링크가 지시하는 파일이름을 찾아준다. EXPR 처리되어 파일이름으로 사용되며 그것의 마지막 구성부분은 심볼릭 링크가 된다. 만약 그것이 심볼릭 링크가 아니거나 심볼릭 링크가 구현되지 않거나 혹은 어떤 시스템 오류가 발생하면, 정의되지 않은 값을 반환하고, 사용자는 $!에 저장된 오류 코드를 조사해야 한다. 만약 EXPR가 명시되지 않으면, 함수는 $_를 사용한다. 반환된 symlink는 사용자가 지정하는 위치에 대해서 상대적임을 유의해야 한다. 예를 들면, 사용자는 다음과 같이 한다:
readlink “/usr/local/src/express/yourself.h”
그리고, readlink는 다음과 같이 결과를 반환할 수도 있다.
../express.1.23/includes/yourself.h
만약 사용자의 현재 디렉토리가 /usr/local/src/express가 아니라면, 파일이름처럼 직접 사용가능 하지는 않다.

recv
recv SOCKET, SCALAR, LEN, FLAGS
이 함수는 소켓을 통해 메시지를 받는다. 즉 명시된 SOCKET 파일핸들로부터 LENGTH 바이트의 데이터를 받아서 변수 SCALAR에 저장하려고 한다. 그 함수는 송신자의 주소를 반환하거나, 오류가 있을 경우 정의되지 않은 값을 반환한다. SCALAR는 실제 읽혀진 데이터 길이에 맞추어 크기가 조절된다. 그 함수는 recv(2)와 같이 같은 플래그를 사용한다. 6장의 “소켓” 부분을 참고하라.

redo
redo LABEL
redo
명령어 redo는 조건을 다시 평가하지 않고 루프를 다시 시작한다. continue 문이 있어도 실행되지 않는다. 만약 LABEL이 생략되었으면 명령어는 가장 안쪽의 루프를 참조한다. 이 명령어는 일반적으로 입력된 것을 감추기를 원하는 프로그램에 주로 사용된다:
# A loop that joins lines continued with a backslash.
LINE: while (<STDIN>) {
    if (s/\e\e\n$// and $nextline = <STDIN>) {
        $_ .= $nextline;
        redo LINE;
    }
    print; # or whatever…
}

ref
ref EXPR
만약 EXPR가 레퍼런스이면 ref 연산자는 참값을 반환하고 그렇지 않으면 널 문자열을 반환한다. 반환값은 레퍼런스가 지시하는 레퍼런스의 종류에 따라 달라진다. 내장형 종류는 다음과 같다:
REF
SCALAR
ARRAY
HASH
CODE
GLOB
만약 레퍼런스된 객체가 패키지로 블레싱(blessing)되면, 그 패키지 이름이 대신에 반환값으로 반환된다. 사용자는 ref를 “typeof” 연산자로 이해할 수 있다.
if (ref($r) eq “HASH”) {
    print “r is a reference to a hash.\n”;
}
elsif (ref($r) eq “Hump”) {
    print “r is a reference to a Hump object.\n”;
}
elsif (not ref $r) {
    print “r is not a reference at all.\n”;
}
더 상세한 내용은 4장을 참고하라.

rename
rename OLDNAME, NEWNAME
이 함수는 파일의 이름을 변경한다. 성공인 경우 1을, 그렇지 않은 경우(그리고 오류 코드를 $!에 저장한다.) 0을 반환한다. 상이한 파일 시스템에서 이 함수는 동작하지 않는다. 만약 NEWNAME을 가진 파일이 이미 존재하면 그 파일이 손상된다.

require
require EXPR
require
이 함수는 자신의 인자와 어느 정도 상호의존성을 가지고 있다. (만약 EXPR이 사용되지 않으면 $_가 인자로 사용된다.)
만약 인자가 문자열이면, 이 함수는 문자열로 표현된 이름을 가진 분리된 파일의 Perl 코드를 포함하여, 실행한다. 이런 점은 단지 라이브러리 파일이 이미 실행될 코드내에 포함되어 있지 않은지 여부를 조사하도록 요구한다는 점만 빼고, 파일의 내용물에 대해 eval 문을 실행하는 것과 유사하다.(따라서, 중복되게 컴파일될 가능성에 대해 걱정할 필요없이 파일의 의존성을 표현하는데 사용될 수 있다) 이 함수는 배열 @INC에 저장된 include 경로를 어떻게 찾아야 하는지 알고 있다. (2장의 “특수 변수” 부분을 참고하라.)
require 함수는 다음 서브루틴과 매우 비슷하게 동작한다:
sub require {
    my($filename) = @_;
    return 1 if $INC{$filename};
    my($realfilename, $result);
    ITER: {
        foreach $prefix (@INC) {
            $realfilename = “$prefix/$filename”;
            if (-f $realfilename) {
                $result = eval `cat $realfilename`;
                last ITER;
            }
        }
        die “Can’t find $filename in \e@INC”;
    }
    die $@ if $@;
    die “$filename did not return true value” unless $result;
    $INC{$filename} = $realfilename;
    return $result;
}
그 파일은 마지막 값이 초기화 코드의 성공적인 실행을 나타내기 위하여 참값을 반환해야 한다. 그래서, 그런 파일은 1로 끝내는 것이 관례이다; 그렇지 않고 만약 사용자가 반환값이 참값인지 확신하지 못하는 경우에. 만약 그 파일이 require 혹은 do EXPR 명령어를 통해 앞서 포함되어 있고, 다시 포함되지 않는다는 점에서, 이 연산자는 이제는 약간 진부하게 되버린 do EXPR 연산자와는 차이가 있다. 그리고, 모든 어려움이 감지되며, 치명적 오류(eval의 사용으로 인한 함정으로 발생된다)가 보고된다. 그러나, 명령어 do@INC의 경로 검색을 어떻게 해야하는 지를 알고 있다. 만약 require 문의 인자가 수이면, 현재 실행되는 Perl 실행파일의 버전 번호($]에 의해 알수 있다)가 EXPR에 비유되며, 그리고 만약 그 인자수가 더 작으면, 실행은 중지된다. 따라서, Perl 버전 5.003을 필요로 하는 스크립트는 첫번째 행에 다음의 코드를 가질 수 있다.
require 5.003;
그리고, 이전버전의 Perl은 실행이 중지될 것이다. 만약 require문이 인자가 패키지 이름(패키지를 참고)이면, require문은 자동적으로 접미사 .pm이 붙는 것으로 가정하며, 표준 모듈을 쉽게 읽어들일 수 있게 해준다. 이러한 점은 컴파일 시가 아니고 실행시에 이루어지며, 임포트(import) 루틴이 호출되지 않는다는 점에서, 이것은 use문과 비슷한다. 예를 들면현재 패키지내에 심볼을 전혀 도입하지 않고 Socket.pm을 사용하려면, 다음과 같이 한다:
require Socket; # “use Socket;”을 대신함
그러나, 다음 코드를 이용하면 같은 효과를 얻을 수 있다. 다음 코드는 만약 Socket.pm을 찾지못하면 컴파일시 경로를 준다는 장점이 있다.
use Socket ();
reset
reset EXPR
reset
이 함수는 일반적으로 루프의 맨 앞이나 루프의 맨 뒤의 continue 블록에서 전역변수의 값을 지우거나 ??검색을 리셋하여 그들이 다시 동작하게끔 사용된다. EXPR은 단일 글자로 이루어진 리스트의 하나로 해석된다(“-“는 범위를 표시하는데 사용할 수 있다). 그런 글자중 하나로 시작되는 모든 스칼라 변수, 배열 그리고 해쉬는 원래의 상태로 리셋된다. 만약 EXPR이 명시되지 않으면, 하나만 일치하는 것을 찾는 검색(?PATTERN?)은 다시 리셋되어 일치검색을 하게된다. 그 함수는 변수를 리셋하거나 현재 패키지만을 검색한다. 그것은 항상 1을 반환한다. 모든 “X”변수를 리셋(reset)하려면 다음과 같이 한다:
reset ‘X’;
모든 소문자 변수를 리셋(reset)하려면 다음과 같이 한다:
reset ‘a-z’;
마지막으로 ?? 검색자를 리셋(reset)하려면 다음과 같이 한다:
reset;
“A-Z”를 초기화하는 경우 ARGV, INC, ENV 그리고 SIG 배열을 완전히 지워버리기 때문에 reset ‘a-z’는 사용하지 않기를 권한다.
my가 선언한 지역변수는 영향을 받지 않는다. reset의 용도는 애매모호하게도 낮게 평가된다.

return
return EXPR
이 함수는 서브루틴(혹은 eval) 수행이 끝난 후 지정된 값을 반환한다.(만약 return 문을 명기하지 않으면 실행된 최종 식의 값이 반환값으로 사용된다.) 서브루틴이나 eval 문 밖에서 return을 사용할 수 없으며 사용할 경우 치명적인 오류가 발생한다. eval 문을 호출한 서브루틴 대신에 eval 문이 반환값을 줄 수 없다는 것을 유의해야 한다.
제공된 식은 서브루틴 실행문에서 처리된다. 즉 서브루틴이 스칼라 구문에서 호출되면 EXPR는 스칼라 구문의 형태로 처리된다. 만약 서브루틴이 리스트 구문에서 호출 되면 EXPR는 리스트 구문의 형태로 처리되며 리스트 값을 반환한다. 인자 없는 return 문은 스칼라 형태의 정의되지 않은 값을 반환하며, 리스트의 형태로는 널 리스트를 반환한다. 서브루틴 호출 문은(잘못 만든 이름이지만) 함수 wantarray를 사용함으로써 서브루틴 내에서 결정된다.

reverse
reverse LIST
이 함수는 리스트 형태의 역순으로 LIST의 요소로 구성된 리스트 값을 반환한다. 이것은 단지 포인터의 위치를 바꿈으로 대단히 효율적이다. 함수는 내림차순 시퀀스(sequences)를 형성하는데 사용된다:
for (reverse 1 .. 10) { … }
해쉬가 리스트로 변환되는 방식 때문에 함수에 전달되는 경우, reverse는 값이 유니크(unique)하다고 가정하고, 해쉬를 반대로 하는데 사용될 수 있다:
%barfoo = reverse %foobar;
함수는 스칼라 형태로 LIST의 모든 요소를 붙여서 그 결과를 글자별 역순으로 반환한다. 힌트: 사용자 정의 함수에 의해 먼저 정렬된 리스트를 역순으로 바꾸는 것은 때때로 단순히 먼저 역방향으로 정렬하는 것으로 쉽게 가능하다.

rewinddir
rewinddir DIRHANDLE
이 함수는 현재의 위치를 DIRHANDLE상의 readdir 루틴에 대하여 디렉토리의 시작부분으로 설정한다. 함수는 readdir를 지원하는 모든 머신에 대해서 반드시 지원되는 것은 아니다.

rindex
rindex STR, SUBSTR, POSITION
rindex STR, SUBSTR
이 함수는 문자열 STR에서 마지막으로 일치하는 문자열 SUBSTR의 위치를 반환한다는 점만 빼고 index와 같이 동작한다(index의 반대). 이 함수는 만약 일치하는 문자열이 없으면 $[-1를 반환한다. $[는 대개 항상 0이므로 이 함수는 대개 항상 1을 반환한다. 만약 POSITION이 지정되면 그것은 가장 우측 위치값을 반환한다. 문자열에 대하여 역방향으로 작업하고자 하면:
$pos = length $string;
while (($pos = rindex $string, $lookfor, $pos) >= 0) {
    print “Found at $pos\n”;
    $pos–;
}

rmdir
rmdir FILENAME
이 함수는 명기된 FILENAME의 디렉토리가 비어 있으면 그 디렉토리를 지운다. 만약 그 실행이 성공하면 1을 반환하고, 실패하면 0을 반환하며 오류 코드를 $!에 저장한다. 만약 FILENAME을 생략하면 함수는 $_를 사용한다.
s///
s///
대치 연산자. 2장 “패턴 일치”를 참고하시오

scalar
scalar EXPR
이 의사 함수는 리스트 구문 내에서의 처리가 다른 결과를 만들 때 LIST내에서 EXPR이 스칼라 형태로 처리되도록 하는데 사용된다. 예를 들면:
local($nextvar) = scalar <STDIN>;
앞의 예제는 값이 할당되기 전에 <STDIN>이 표준입력으로부터 모든 행을 읽지 않도록 한다. 왜냐하면, local 리스트에 값의 할당은 리스트 구문을 제공하기 때문이다.(이 예제에서 scalar를 사용하지 않으면 <STDIN>의 첫번째 행은 여전히 $nextvar의 값을 가진다. 그러나, 그 다음에 오는 행들은 읽혀지고 나서 처리된 후 바로 없어진다. 그 이유는 할당 작업이 리스트에서 행해지기 때문이다. 리스트는 단지 단일 스칼라 값을 받을 수 있기 때문이다.)
물론 타이핑을 적게하는 단순한 방법은 괄호는 없애는 것이다. 그렇게 함으로써 리스트 구문을 스칼라 구문으로 변환할 수 있다:
local $nextvar = <STDIN>;
print 함수는 LIST 연산자이므로 다음과 같이 해야 한다.
print “Length is “, scalar(@ARRAY), “\n”;
만약 @ARRAY의 길이를 출력하고자 한다면 앞의 예제와 같이 하면 된다. 리스트 구문 내에서는 처리를 강제로 할 필요는 없다. 왜냐하면 리스트를 원하는 모든 연산은 이미 리스트 구문을 그것의 리스트 인자로 제공하기 때문이다. 따라서, scalar에 해당하는 리스트 함수는 존재하지 않는다.

seek
seek FILEHANDLE, OFFSET, WHENCE
이 함수는 표준 입출력 함수 fseek(3) 호출처럼 FILEHANDLE의 파일 포인트 위치를 설정한다. 파일의 맨 처음 위치는 옵셋 값이 1이 아니라 0이며 옵셋 값은 행 수가 아니라 바이트 단위의 값을 위미한다.(대개 행 수는 변환하기 때문에, 만약 사용자의 모든 행들이 특정한 길이가 아니라면 혹은 행 수를 바이트 옵셋으로 변환하는 인덱스를 만들지 않으면, 대개 각 행 길이는 다르므로, 그 행 번호까지 파일을 검색하기 않고 특정한 행 번호에 접근하는 것은 불가능하다..)
FILEHANDLE은 파일핸들의 이름이나 파일핸들 객체의 레퍼런스를 값으로 주는 식일 수도 있다. 이 함수는 성공시에 1을 그렇지 않은 경우에는 0을 반환한다. 편의상 그 함수는 파일의 임의의 위치에서부터 옵셋 값을 계산할 수 있다. WHENCE의 값은 OFFSET의 값이 계산되는 상대적인 파일 위치를 나타낸다: 즉 파일의 맨 앞 위치는 0; 파일의 현재 위치는 1, 그리고 파일의 맨 끝 위치는 2이다. WHENCE의 값이 1이나 2인 경우에는 OFFSET의 값은 음수일 수 있다. 이 함수를 재미있게 사용하는 예는 다음과 같이 커지는 파일을 사용자가 추적하는 것이다:
for (;;) {
    while (<LOG>) {
        … # 파일 프로세스
    }
    sleep 15;
    seek LOG,0,1; # EOF 오류를 리셋
}
맨 마지막 seek은 포인터를 옮기지 않고 EOF 오류(End-Of-File error)를 지운다. 만약 그렇게 동작하지 않으면(사용자의 C 라이브러리의 표준 입출력 구현에 따라 달라진다), 사용자는 다음과 같은 것이 필요할 수 있다.
for (;;) {
    for ($curpos = tell FILE; $_ = <FILE>; $curpos = tell FILE) {
        # search for some stuff and put it into files
    }
    sleep $for_a_while;
    seek FILE, $curpos, 0;
}
비숫한 전략은 배열의 각 행에 대한 seek 주소를 기억하는데 사용될 수 있다.

seekdir
seekdir DIRHANDLE, POS
이 함수는 DIRHANDLE상의 readdir 루틴의 현재 위치를 설정한다. POStelldir의 반환값이어야 한다. 이 함수는 상응하는 시스템 라이브러리 루틴처럼 가능한 디렉토리 압축에 대해서 같은 특성을 가진다. 이 함수는 readdir이 지원되는 시스템이라도 반드시 지원되는 것은 아니다. 그리고 readdir이 지원되지 않은 시스템에서는 이 함수는 지원되지 않는다.

select (output filehandle)
select FILEHANDLE
select
역사적인 이유로 서로 상관 관계가 전혀 없는 2개의 select 연산자가 존재한다. 다른 select에 대한 것은 뒤에서 설명한다. 이 select 연산자는 현재 선택된 출력 파일핸들을 반환하고, 만약 FILEHANDLE이 제공되면 현재의 디폴트 파일핸들을 출력으로 설정한다. 이러한 것은 2가지 효과가 있다: 첫째 파일핸들 없이 write 혹은 print 실행은 이 파일핸들을 디폴트로 출력한다. 둘째 출력과 관련된 특수 변수들은 바로 이 출력 파일핸들을 참조하게 된다. 예를 들면, 만약 사용자가 2개 이상의 출력 파일핸들을 같은 top-of-form 포맷으로 설정해야 한다면 다음과 같이 할 수 있다:
select REPORT1;
$^ = ‘MyTop’;
select REPORT2;
$^ = ‘MyTop’;
그러나, 앞의 예는 REPORT2를 현재 선택된 파일핸들로 만든다는 점을 유의해야 한다. 이런점이 반사회적인 면으로 해석될 수도 있다. 왜냐하면 그것은 어떤 다른 루틴의 print 혹은 write 문을 혼란에 빠뜨릴 수 있기 때문이다. 적절히 작성된 라이브러리 루틴들은 현재 선택된 파일핸들이 종료시 마치 항목의 하나인 것처럼 그대로 존재 하게끔 한다. 이것을 지원하기 위해 FILEHANDLE은 실제 파일핸들의 이름을 얻을 수 있는 식이 될 수도 있다. 따라서, 사용자는 현재 선택된 파일핸들을 저장하거나 복구할 수 있다:
my $oldfh = select STDERR; $| = 1; select $oldfh;
혹은 (당혹스럽고 불명확하지만):
select((select(STDERR), $| = 1)[0])
앞의 예제는 select(STDERR)의 반환값(이것은 부작용으로 STDERR을 선택한다)과 $| = 1(이것은 항상 1이다. 그러나, 부작용으로 현재 선택된 STDERR에 대한 오토플러쉬(autoflushing)을 설정한다.)로 구성된 리스트를 생성함으로써 동작하게 된다. 그 리스트의 첫번째 요소(앞서 선택된 파일핸들)는 이제 바같쪽 select 문의 인자로 사용된다. 이러한 점이 당황스럽지만 옳지 않은가? 바로 그것인 리스프(Lisp) 언어가 위험하다는 사실을 충분히 깨닫게 해주는 것이다.
그러나, 지금까지 모든 점에 대해 설명했으므로 이제는 select 문의 이러한 쓰임새가 거의 필요 없다는 점을 알아야 한다. 왜냐하면 사용자가 설정하고자 하는 특수 변수의 대부분이 객제 지향적인 포괄 방법을 사용하기 때문이다. 따라서 $|를 직접 설정하는 것보다 다음과 같이 한다.
use FileHandle;
STDOUT->autoflush(1);
그리고 초기 포맷 예제는 다음과 같이 작성될 수 있다:
use FileHandle;
REPORT1->format_top_name(“MyTop”);
REPORT2->format_top_name(“MyTop”);
select (ready file descriptors)
select RBITS, WBITS, EBITS, TIMEOUT
4개 인자의 select 연산자는 앞서 설명한 연산자와는 전혀 관련이 없다. 이 연산자는 사용자의 파일 기술자중 어느 것이 입력 혹은 출력으로 사용될 수 있는지의 결정이나 혹은 예외적 조건을 리포팅(reporting)하기 위해 사용된다.(이것은 사용자가 폴링(polling)을 하지 않아도 되도록 해준다.) 이 함수는 사용자가 지정한 비트마스크(bitmasks)와 함께 시스템 호출 select(2)를 이용할 수 있다. 비트마스크(bitmasks)는 다음과 같이 filenovec을 사용하여 만들 수 있다:
$rin = $win = $ein = “”;
vec($rin, fileno(STDIN), 1) = 1;
vec($win, fileno(STDOUT), 1) = 1;
$ein = $rin | $win;
만약 사용자가 많은 파일핸들에 대해 select 문을 사용하고자 하면 사용자는 다음과 같은 서브루틴을 작성할 수 있다:
sub fhbits {
    my @fhlist = @_;
    my $bits;
    for (@fhlist) {
        vec($bits, fileno($_), 1) = 1;
    }
    return $bits;
}
$rin = fhbits(qw(STDIN TTY MYSOCK));
만약 사용자가 같은 비트마스크(bitmasks)를 반복해서 사용하고자 하면(그리고 그렇게 하면 훨씬 효율적이다) 관례상 다음과 같이 한다:
($nfound, $timeleft) = select($rout=$rin, $wout=$win, $eout=$ein, $timeout);
혹은 어떤 파일 기술자가 준비되기 까지 차단하기 위해서:
$nfound = select($rout=$rin, $wout=$win, $eout=$ein, undef);
할당문의 값이 왼쪽에 위치하므로 $wout=$win 트릭은 동작한다. 그 결과 $wout는 할당문에 의해 먼저 처리되고 나서 select 문에 의해 처리되며 $win은 변환하지 않는다. 모든 비트마스크는 또한 undef 될 수 있다. 만약 타임아웃이 초단위 값을 가지도록 명시되어 있으면, 그것은 1초이하의 값을 가질 수 있다.(타임아웃이 0이면 폴(poll)에 영향을 미친다.) 구현되어 있는 많은 것들이 $timeleft를 반환할 수 있는 것은 아니다. 만약 그렇지 않다면, 그것은 항상 제공된 $timeout와 값이 같은 $timeleft를 반환한다. select의 한가지 사용 방법은 슬립(sleep)이 허용하는 것보다 더 정교한 값으로 슬립(sleep)하는 것이다. 이것을 하기 위해서는 모든 비트마스크에 대해 undef 문을 지정해야 한다. 그래서, (적어도) 4.75초동안 슬립(sleep)하기 위해서는 다음과 같이 한다:
select undef, undef, undef, 4.75;
(몇몇 비 유닉스 시스템에서는 이것이 동작하지 않을 수 있다. 그리고 사용자는 준비되지 않은 유효한 기술자를 위하여 적어도 1개의 비트마스크(bitmask)를 조작해야 한다.) 4개 인자의 select 문을 버퍼화된 입출력(읽기 <HANDLE>처럼)과 섞어서 사용하는 것은 문제를 만들 뿐이다.

semctl
semctl ID, SEMNUM, CMD, ARG
이 함수는 System V IPC 시스템 호출 semctl(2)를 이용한다. 만약 CMD가 &IPC_STAT 혹은 &GETALL이면 ARG는 반드시 반환값인 semid_ds 구조 혹은 세마포어(semaphore) 값 배열을 저장할 변수이어야 한다. 이 함수는 ioctl와 같은 반환값을 준다: 오류가 발생한 경우 정의되지 않은 값을, 0인 경우 “0 이지만 참”, 또는 0이 아닌 경우에는 실제 반환값을 준다. 오류가 발생한 경우 그 함수는
require “ipc.ph”;
require “sem.ph”;
이 함수는 시스템 V IPC를 지원하는 머신에서만 존재한다.

semget
semget KEY, NSEMS, SIZE, FLAGS
이 함수는 System V IPC 시스템 호출 semget(2)를 이용한다. 이 함수는 세마포어(semaphore) ID를 반환하거나 오류가 발생시에는 정의되지 않은 값을 반환한다. 오류가 발생시에는 그 함수는 오류 코드를 $!에 저장한다. 호출전에 사용자는 다음과 같이 선언해야 한다..
require “ipc.ph”;
require “sem.ph”;
이 함수는 시스템 V IPC를 지원하는 머신에서만 지원한다.

semop
semop KEY, OPSTRING
이 함수는 신호보내기와 대기와 같은 세마포어(semaphore)를 수행하기 위해 System V IPC 시스템 호출 semop (2)를 이용한다. OPSTRING는 반드시 semop 구조의 배열이어야 한다. 사용자는 pack(“s*”, $semnum, $semop, $semflag) 연산을 통해 각 semop구조를 생성할 수 있다. 세마포어(semaphore) 연산의 횟수는 OPSTRING의 길이를 통해 알 수 있다. 이 함수는 성공시에 참값을, 그렇지 않고 오류가 발생시에는 거짓값을 반환한다. 오류가 발생시에는 오류 코드를 $!에 저장한다. 호출전에 사용자는 다음과 같이 선언해야 한다.
require “ipc.ph”;
require “sem.ph”;
다음 코드는 세마포어 ID 변수 $semid의 세마포어(semaphore) 변수 $semnum를 기다린다:
$semop = pack “s*”, $semnum, -1, 0;
die “Semaphore trouble: $!\n” unless semop $semid, $semop;
세마포어(semaphore)에 신호를 보내기 위해서는 단지 -1을 1로 대치하면 된다. 이 함수는 시스템 V IPC를 지원하는 머신에서만 지원한다.

send
send SOCKET, MSG, FLAGS, TO
send SOCKET, MSG, FLAGS
이 함수는 소켓을 통해 메시지를 보낸다. 이 함수는 같은 이름의 시스템 호출과 같은 플래그를 사용한다 – send(2)를 참고하라. 연결되지 않은 소켓상에서 사용자는 보낼 목적지인 TO를 명시해야 한다. 이 경우에 sendsendto(2)처럼 동작한다. 함수는 보낸 바이트 수를 반환하거나 오류 발생시에는 정의되지 않은 값을 준다. 오류 발생시에 함수는 오류 코드를 $!에 저장한다.(어떤 비 유닉스 시스템에서는 일반 파일 기술자와는 다른 객체처럼 소켓을 부적절하게 처리한다. 그 결과 사용자는 간편한 표준 입출력 연산자보다는 sendrecv 문을 항상 소켓에 사용해야 된다.)

setpgrp
setpgrp PID, PGRP
이 함수는 현재 프로세스 그룹(pgrp)을 지정된 PID(현재 프로세스에 대해서는 0인 PID를 사용)로 설정한다. setpgrp(2)가 지원되지 않는 기계에서 setpriority를 실행하면 치명적 오류가 발생한다. 주의사항: 어떤 시스템은 사용자가 제공하는 인자를 무시하고 항상 setpgrp(0, $$)를 수행한다. 다행히도 사용하는 인자들이 대개 사용자가 제공하는 것들이다.(더 나은 이식성을 위해서는(어떤 정의에 의하면) POSIX 모듈의 setpgid() 함수를 사용하라. 혹은 사용자의 스크립트를 데몬처럼 실행시키려 한다면 POSIX::setsid() 함수를 고려하라.)

setpriority
setpriority WHICH, WHO, PRIORITY
이 함수는 프로세스, 프로세스 그룹 혹은 사용자의 현재 우선순위를 설정한다. setpriority(2)를 참고하라. setpriority(2)가 지원되지 않는 기계에서 setpriority를 실행하면 치명적 오류가 발생한다. 사용자 프로세스의 4단위를 낮추려면(사용자 프로그램을 nice(1)명령어와 함께 실행하는 것과 같음) 다음과 같이 한다:
setpriority 0, 0, getpriority(0, 0) + 4;
주어진 우선순위의 해석은 OS 마다 다르다.

setsockopt
setsockopt SOCKET, LEVEL, OPTNAME, OPTVAL
이 함수는 요구되는 소켓 선택사양의 값을 설정한다. 오류가 발생하는 경우 함수는 정의되지 않은 값을 반환한다. 만약 사용자가 인자를 전달하지 않으려면 OPTVAL의 값을 undef로 설정해야 한다. 포트상의 바로 전 TCP 연결 소켓이 막 단절하려는 상태에 있는 경우, 특정 주소에 바인딩 할수 없는 문제를 해결하기 위해서 사용되는 일반적인 선택사양은 SO_REUSEADDR이다. 다음은 그와 같은 경우에 사용하는 예를 보여준다:
use Socket;
setsockopt(MYSOCK, SOL_SOCKET, SO_REUSEADDR, 1)
or warn “Can’t do setsockopt: $!\n”;

shift
shift ARRAY
shift
이 함수는 배열의 첫번째 값을 제거하고, 배열의 요소 개수를 하나 줄이고 나머지 요소를 앞쪽으로 이동시키며, 제거된 값을 반환한다.(앞쪽으로 이동시킨다는 표현은 사용자가 배열 리스트의 모습을 어떻게 형상화 하는가에 따라 달라질 수 있다.) 만약 배열의 요소가 존재하지 않으면 함수는 정의되지 않은 값을 반환한다. 만약 ARRAY를 생략하면 함수는 @ARGV(주 프로그램의)나 @_(서브루틴의)에 대해 shift 연산을 행한다. 또한 nshift, push, pop, 그리고 splice도 참고하라. poppush 함수가 우측단으로 연산이 행해지며 shiftunshift 함수는 배열의 좌측단으로 같은 연산이 행해진다.

shmctl
shmctl ID, CMD, ARG
이 함수는 시스템 V IPC의 시스템 호출 shmctl(2)를 이용한다. 만약 CMD가 &IPC_STAT이면 ARG는 반환값인 shmid_ds 구조를 포함한 변수이어야 한다. 그 함수는 ioctl와 같이 반환값을 준다: 오류가 발생한 경우 정의되지 않은 값을, 0인 경우 “0 이지만 참값”을 그리고 그 밖의 경우에는 실제 반환값을 준다. 오류가 발생한 경우 함수는 오류 코드를 $!에 저장한다. 호출하기 전에 사용자는 다음과 같이 해야 한다.
require “ipc.ph”;
require “shm.ph”;
이 함수는 시스템 V IPC를 지원하는 기계에서만 존재한다.

shmget
shmget KEY, SIZE, FLAGS
이 함수는 시스템 V IPC의 시스템 호출 shmget(2)를 이용한다. 이 함수는 공유 메모리 세그먼트 ID 혹은 오류가 있는 경우 정의되지 않은 값을 반환한다. 오류가 발생한 경우 함수는 오류 코드를 $!에 저장한다. 호출하기 전에 사용자는 다음과 같이 해야 한다.
require “ipc.ph”;
require “shm.ph”;
이 함수는 시스템 V IPC를 지원하는 머신에서만 존재한다.

shmread
shmread ID, VAR, POS, SIZE
이 함수는 SIZE 크기이며 POS위치에서 시작하는 공유 메모리 세그먼트 ID로 부터 데이터를 읽는다(즉 추가하거나 복사하거나 빼거나함으로써). VAR는 읽혀진 데이터를 저장하는 변수이어야 한다. 이 함수는 성공인 경우 참값을 그렇지 않고 오류가 발생한 경우 거짓값을 반환한다. 오류 발생시 이 함수는 오류 코드를 $!에 저장한다. 이 함수는 시스템 V IPC를 지원하는 머신에서만 존재한다.

shmwrite
shmwrite ID, STRING, POS, SIZE
이 함수는 SIZE크기이며 POS위치에서 시작하는 공유 메모리 세그먼트 ID로 부터 데이터를 쓴다(즉 추가하거나 복사하거나 빼거나함으로써).만약 STRING이 너무 길면 SIZE 바이트만 사용된다. 만약 STRING이 너무 짧으면 SIZE 바이트의 크기를 널로 채운다. 이 함수는 성공인 경우 참값을 그렇지 않고 오류가 발생한 경우 거짓값을 반환한다. 오류가 발생시 이 함수는 오류 코드를 $!에 저장한다. 이 함수는 시스템 V IPC를 지원하는 머신에서만 존재한다.

shutdown
shutdown SOCKET, HOW
이 함수는 HOW로 표시된 방법으로 소켓 연결을 절단한다. 만약 HOW가 0이면 더 이상의 수신은 허용되지 않는다. 만약 HOW가 1이면 더 이상의 송신이 허용되지 않는다. 만약 HOW가 2이면 모든 것이 허용되지 않는다.(만약 지금 시스템을 어떻게 종료시키는지를 이해하고자 한다면 외부 프로그램을 실행해본다. system을 참고하라.)

sin
sin EXPR
여기서는 이 연산자에 관해 특별한 것을 설명할 것이 없다. 이 함수는 단지 EXPR(단위는 라디안)의 사인값을 반환한다. 만약 EXPR이 명시되지 않으면 $_의 사인값을 준다. 역사인 연산을 하려면 POSIX::asin() 함수를 사용하거나 다음의 관계를 이용하면 된다.
sub asin { atan2($_[0], sqrt(1 – $_[0] * $_[0])) }

sleep
sleep EXPR
sleep
이 함수는 스크립트가 EXPR(단위: 초)동안 슬립(sleep)하도록 하거나 또는 EXPR을 생략하면 영원히 슬립(sleep)하도록 한다. 이 때 프로세스에 SIGALRM을 신호를 보내어 중지시킬 수 있다. 그 함수는 실제 휴면한 시간(단위: 초)을 반환한다. 몇몇 시스템에서는 그 함수는 “초단위의 끝까지”까지 슬립한다. 그래서, 예를 들면, sleep 1은 사용자가 슬립을 시작한 시간에 따라 달라지며, 0과 1초 사이에서 슬립(sleep)한다. sleep 2는 1에서 2초 사이에는 슬립(sleep)한다. 만약 select (준비된 파일 기술자)호출이 존재하고, 그것을 호출할 수 있다면, 사용자는 더 나은 해결책을 가질 수 있다. 사용자는 유닉스 시스템이 지원하는 getitimer(2)setitimer(2)를 호출하기 위해 syscall을 사용할 수도 있다.

socket
socket SOCKET, DOMAIN, TYPE, PROTOCOL
이 함수는 명시된 종류의 소켓을 열어서 파일핸들 SOCKET과 연결시킨다.
DOMAIN, TYPE 그리고 PROTOCOLsocket(2)처럼 명시된다. 이 함수를 사용하기 전에 사용자의 프로그램은 다음의 코드를 반드시 내포해야 한다.
use Socket;
앞의 코드는 사용자에게 적절한 상수를 준다. 성공적이면 그 함수는 참값을 반환한다. 6장의 “소켓” 부분의 예제를 참조하시오

socketpair
socketpair SOCKET1, SOCKET2, DOMAIN, TYPE, PROTOCOL
이 함수는 지정된 종류의 지정된 도메인내에서 무명의 소켓쌍을 생성한다. DOMAIN, TYPE, 그리고 PROTOCOLsocketpair(2) 경우 처럼 지정된다. 만약 socketpair(2)이 지원되지 않으면 이 함수를 실행하면 치명적 오류가 발생한다. 이 함수는 성공인 경우 참값을 반환한다. 이 함수는 대개 fork 문 바로 전에 사용된다. 그 결과로 생성되는 프로세스들 중 하나는 SOCKET1을 닫아야 하며, 나머지 하나는 SOCKET2를 닫아야 한다. 사용자는 pipe 함수에 의해 생성된 파일핸들과는 달리 이 소켓들을 양방향으로 사용할 수 있다.

sort
sort SUBNAME LIST
sort BLOCK LIST
sort LIST
이 함수는 LIST를 정렬한다. 함수는 디폴트로 표준 문자열 비교 순서 형태로 정렬을 한다(정의된 널 문자열에 앞서 정의되지 않은 값이 정렬된다. 정의된 널 문자열은 그밖의 다른 것에 앞서 정렬된다.) 만약 SUBNAME이 주어지면, 그것은 리스트의 요소들이 어떻게 정렬되는가에 따라서 0보다 작은 혹은 0인 또는 0 보다 큰 값을 반환하는 서브루틴의 이름이 된다.(편리한 <=>와 cmp 연산자를 사용하여 3개의 원소로 구성된 숫자 그리고 문자열 비교 연산을 할 수 있다.) 효율성 측면에서 서브루틴을 위한 일반적인 호출 코드는 다음과 같은 영향을 끼치면서 그냥 넘어간다: 서브루틴은 재귀적인 서브루틴이 아니며, 비교할 2개의 요소는 @_의 형태가 아니라 $a와 $b의 형태로 서브루틴에 전달된다(아래 예제를 참고하라) 변수 $a와 $b는 레퍼런스 형태로 전달된다. 따라서 서브루틴 내에서 그 변수들을 변경하지 말라. SUBNAME은 스칼라 변수명(unsubscripted)이며 그럴 경우 그 값은 실제 사용할 서브루틴의 이름을 제공한다. 사용자는 SUBNAME 대신에 익명의 인행(in-line:그때마다 즉시 처리하는) 서브루틴으로 사용할 BLOCK을 사용할 수 있다. 일반적인 숫자 정렬을 하기 위해서는 다음과 같이 한다:
sub numerically { $a <=> $b; }
@sortedbynumber = sort numerically 53,29,11,32,7;
내림차순으로 정렬하기 위해서는, 단지 $a와 $b를 뒤바꾸면 된다. 정렬 루틴에서 해쉬 참조를 사용하라. 연관된 값(associated value)을 기준으로 리스트 값을 정렬하려면 sort 루틴의 해쉬 검색을 이용하라:
sub byage {
    $age{$a} <=> $age{$b};
}
@sortedclass = sort byage @class;
이러한 생각을 확장하면, 간편한 비교 연산자를 사용하여 여러 개의 다른 비교를 연속하여 사용할 수 있다. 비교 연산자가 0을 반환할 때, 이는 다음 경우에 속하므로 이들 연산자는 앞의 경우에도 잘 동작한다. 아래 루틴은 먼저 부유한 사람 그리고 키가 큰 사람 그리고 젊은 사람 그리나 나서 알파벳 이름 순서로 구성된 사람의 리스트로 정렬한다. 여기서 그 순서가 잘 정의될 수 있도록 최종적인 $a와 $b사이의 비교 연산을 한다.
sub prospects {
    $money{$b} <=> $money{$a}
    or
    $height{$b} <=> $height{$a}
    or
    $age{$a} <=> $age{$b}
    or
    $lastname{$a} cmp $lastname{$b}
    or
    $a cmp $b;
}
@sortedclass = sort prospects @class;
대소문자 구별없이 필드를 정렬하기 위해서는 다음과 같이 한다:
@sorted = sort { lc($a) cmp lc($b) } @unsorted;
그리고 최종적으로 역순으로 정렬하는 두가지 방법이 동일한 결과을 얻게 됨을 유의하라:
sub backwards { $b cmp $a; }
@harry = qw(dog cat x Cain Abel);
@george = qw(gone chased yz Punished Axed);
print sort @harry; # AbelCaincatdogx를 출력
print sort backwards @harry; # xdogcatCainAbel를 출력
print reverse sort @harry; # xdogcatCainAbel를 출력
print sort @george, “to”, @harry; # 그것이 하나의 LIST임을 기억하라
# AbelAxedCainPunishedcatchaseddoggonetoxyz를 출력
변수 $a와 $b를 my를 사용하여 지역 변수로 선언하지 마라. 그것들은 전역 패키지이다(비록 그것들은 사용자가 use strict 문을 사용시 코드 전역에 걸친 일반적인 제한으로부터 예외로 처리되지만) 사용자는 자신의 sort 루틴이 같은 패키지내에 존재하는지 여부를 확인하거나 호출자의 패키지 명으로 $a와 $b의 값을 정의할 필요는 없다. 주의사항: Perl의 sort문은 C언어의 qsort(3)함수의 관점에서 구현되었다. 만약 사용자의 sort 서브루틴이 일관성 없이 값의 순서를 제공하면 특정 버전의 qsort(3)는 코어 파일을 생성한다.

splice
splice ARRAY, OFFSET, LENGTH, LIST
splice ARRAY, OFFSET, LENGTH
splice ARRAY, OFFSET
이 함수는 OFFSETLENGTH로 지정된 배열의 요소를 제거하고, LIST가 명시되어 있고 관련 요소가 존재하면 제거된 요소대신에 그 요소를 채운다. 함수는 배열로부터 제거된 요소를 반환한다. 필요하면 배열의 크기는 조절된다. 만약 LENGTH가 생략되면 함수는 OFFSET으로부터 시작하여 모든 것을 제거한다. 다음은 기능상 동일하다 ($[는 0으로 가정하고):

직접 방식

동일한 splice

push(@a, $x, $y)

splice(@a, $#a+1, 0, $x, $y)

pop(@a)

splice(@a, -1)

shift(@a)

splice(@a, 0, 1)

unshift(@a, $x, $y)

splice(@a, 0, 0, $x, $y)

$a[$x] = $y

splice(@a, $x, 1, $y)

함수 splice는 서브루틴에 전달되는 리스트 요소를 분할하는데 편리하다. 예를 들면, 리스트 길이가 리스트에 앞서 전달된다고 가정하면 :
sub list_eq { # 2개의 리스트 값을 비교
    my @a = splice(@_, 0, shift);
    my @b = splice(@_, 0, shift);
    return 0 unless @a == @b; # 같은 길이?
    while (@a) {
        return 0 if pop(@a) ne pop(@b);
    }
    return 1;
}
if (list_eq($len, @foo[1..$len], scalar(@bar), @bar)) { … }
그러나, 이 경우에는 레퍼런스를 사용하는 것이 아마 깨끗한 모양이 될 것이다.

split
split /PATTERN/, EXPR, LIMIT
split /PATTERN/, EXPR
split /PATTERN/
split
이 함수는 구분자를 사용하여 EXPR로 주어진 문자열을 검색한 후, 몇 개의 문자열로 분리된 스칼라 구문 형태의 서브문자열 개수나 리스트 구문 형태의 리스트 값을 반환한다. 구분자는 PATTERN으로 주어진 정규식을 사용하여 반복되는 패턴 일치에 의해 결정되며, 구분자의 크기는 제한이 없으며 일치되는 문자열이 같은 형태일 필요는 없다.(구분자는 일반적으로 반환값으로 주어지지 않으나 아래를 참조하시오.) 만약 PATTERN이 전혀 일치하지 않으면 split은 원래의 문자열을 하나의 서브문자열으로 처리하여 반환한다. 만약 하나가 일치하면 2개의 서브문자열을 반환한다.
만약 LIMIT가 명시되고 음수가 아니면, 함수는 문자열을 그 개수만큼만 분할한다(비록 그 개수만큼 분할되지 않더라도). 만약 LIMIT가 음수이면 임의의 큰 값을 LIMIT로 사용한다. 만약 LIMIT를 생략하면 결과물에서 뒷 부분의 널 필드는 제거된다(pop 함수를 사용하는 사용자는 기억해야 할 것이다). 만약 EXPR를 생략하면 함수는 $_의 문자열을 분할한다. 만약 PATTERN 역시 명시되지 않으면 함수는 문자열의 앞부분에 있는 공백을 제거한 후, 공백, /\s+/를 구분자로 사용하여 분할한다.
어떠한 길이의 문자열도 분할 가능한다:
@chars = split //, $word;
@fields = split /:/, $line;
@words = split ‘ ‘, $paragraph;
@lines = split /^/m, $buffer;
널 문자열이나 널 문자열 보다 긴 무엇인가(예를 들어 * 나 ? 에 의해 변형된 단일 문자로 구성된 패턴)와 일치하는 패턴은 EXPR을 일치하는 널 문자열 부분과 아닌 부분들로 분리해 낼 수 있다. 널문자가 아닌 부분에 대한 일치는 일상적인 형태로 구분자를 그냥 넘어간다.(즉 길이가 0인 것과 일치한다 하더라도 그 패턴은 2곳 이상에서 일치하지는 않는다.)
예를 들면:
print join ‘:’, split / */, ‘hi there’;
는 “h:i:t:h:e:r:e”를 출력한다.
공백은 구분자의 하나로 해석되어 출력 결과에는 나타나지 않는다. 아주 단순한 예를 들면 널 패턴 //는 문자들을 단지 분리한다(그리고 공백은 나타나지 않는다).
인자 LIMIT는 문자열을 부분적으로 분리하는데 사용된다:
($login, $passwd, $remainder) = split /:/, $_, 3;
작성한 프로그램 코드가 쉽게 이해될 수 있도록 가급적이면 이처럼 이름들로 구성된 리스트를 분리해야 한다.(오류 검사의 목적으로, 만약 필드의 수가 3개를 넘으면 $remainder는 정의되지 않는다는 점을 유의하라.) 리스트에 값을 할당할 때, 만약 LIMIT를 생략하면 불필요한 작업을 피하기 위해, Perl은 리스트의 변수의 수보다 1이 큰 값인 LIMIT를 제공한다. 위 분리 작업의 경우는 LIMIT는 디폴트로 4가 할당된다. 그리고 $remainder는 나머지 필드 전부가 아니라 3번째 필드의 내용만을 저장한다. 시간을 다투는 응용프로그램에서는 필요한 필드 이상으로 문자열을 분리해서는 안된다.
앞에서 이미 구분자는 반환값으로 주지 않는다고 얘기했다. 그러나, 만약 PATTERN이 괄호를 포함하면 각 괄호쌍에 있는 것과 일치하는 서브문자열이 일반적으로 반환되는 필드로 나누어진 그 결과 리스트에 포함된다. 다음은 단순한 예제이다:
split /([-,])/, “1-10,20”;
는 리스트 값을 생성한다:
(1, ‘-‘, 10, ‘,’, 20)
비록 각 쌍이 일치하지 않더라도, 이는 정의되지 않은 값이 그 위치에서 반환되는 경우라도 괄호를 사용하여 필드는 쌍으로 반환될 수 있다. 따라서 만약 다음과 같이 하면:
split /(-)|(,)/, “1-10,20”;
다음의 값을 얻을 수 있다:
(1, ‘-‘, undef, 10, undef, ‘,’, 20)
하나의 식이 실행시 변환하는 패턴을 지정하기 위해 /PATTERN/으로 사용될 수 있다.(실행을 위해 단지 한번의 컴파일을 하기 위해서는 /$variable/o를 사용하라.) 특별한 경우로, 스페이스를 ” “로 지정하면 인자 없이 split 문이 동작하는 것 처럼 공백문자상에 split 문이 동작한다. 따라서, split(” “)는 awk의 디폴트 동작을 구현하게 된다. 반면에 split(/ /)는 선두에 공간이 많이 존채하는 것 처럼 많은 널 초기 필드를 사용자에게 제공한다.(이런 특수한 경우를 제외하고 만약 정규식 대신에 문자열을 제공하면 그것은 무조건 정규식으로 해석된다.)
다음 예제는 RFC-822메시지 헤드를 분리하여 $head{Date}, $head{Subject}등으로 구성된 해쉬를 생성한다. 구분자는 구분된 필드와 교대로 위치한다는 사실에 근거하여, 해쉬에 쌍의 리스트를 할당하는 트릭을 사용한다. 그것은 반환되는 리스트 값의 한 부분으로 각 구분자 부분을 반환하기 위하여 괄호를 이용한다.
split 패턴은 한쌍의 괄호를 내포함으로써 그 반환값이 쌍의 형태를 보장받기 때문에, 해쉬 할당이 키/값의 쌍으로 구성되는 리스트를 수신을 보장받는다. 이 때 각 키는 헤드 필드의 이름이다. (불행히도 이 방법은 Received-By lines처럼 같은 키 필드를 사용하면 여러 행에 대한 정보를 잃어버린다.)
$header =~ s/\n\s+/ /g; # 연속하는 행들을 하나로 묶는다.
%head = (‘FRONTSTUFF’, split /^([-\w]+):/m, $header);
다음 예제는 유닉스 패스워드 파일의 항목들을 처리한다. 만약 $shell이 가지는 데이터의 맨 끝에 개행문자가 있다면 chop 문을 없애도 좋다.
open PASSWD, ‘/etc/passwd’;
while (<PASSWD>) {
    chop; # 맨뒤 개행 문자를 제거한다.
    ($login, $passwd, $uid, $gid, $gcos, $home, $shell) = split /:/;
    …
}
split 문의 반대 기능은 join 문을 사용하면 된다(join 문은 모든 필드 사이의 구분자가 다 같은 것을 사용한다는 점이 다르다). 문자열을 지정된 위치의 필드로 분리하기 위해서는 unpack을 사용한다.

sprintf
sprintf FORMAT, LIST
이 함수는 printf 문의 사용 규칙으로 생성된 정형화된 문자열을 반환한다. 문자열 FORMAT은 삽입된 필드 지정자와 텍스트를 포함하며, 그 필드 지정자는 LIST의 각 요소에 대응되어 그 기능을 수행한다. 필드 지정자는 대개 다음과 같은 포맷을 취한다:
%m.nx
여기서 mn은 선택이 가능한 크기값으로 필드 종류에 따라 그 해석이 달라진다. 그리고 x는 다음중 하나의 의미를 가진다:

코드

의미

c

글자

d

10진수

e

지수 포맷의 부동소솟점

f

고정점 포맷의 부동소숫점

g

콤팩트 포맷의 부동소숫점

ld

(Long) 10진수

lo

(Long) 8진수

lu

(Long) 언사인드(unsigned) 10진수

lx

(Long) 16진수 정수

o

8진수

s

문자열

u

언사인드(unsigned) 10 정수

x

16진수

X

대문자인 16 정수

다양한 조합에 대한 설명은 매뉴얼 페이지(manpage)의 printf(3)에 충분히 있다. 그러나, m은 대개 최소 필드 길이(좌측 정렬의 경우에는 음수값)이며, n은 지수함수 포맷의 정확도(precision) 그리고 다른 포맷에 대한 최대 길이이다. 대개 채워지지 않은 문자열에 대해서는 공백문자가 숫자에 대해서는 0이 더해진다. 길이 지정자로 * 문자를 사용하는 것은 지원되지 않는다. 그러나, 다음과 같이 사용자는 쉽게 FORMAT에 길이 식을 포함하여 이 문제를 해결할 수 있다:
$width = 20; $value = sin 1.0;
foreach $precision (0..($width-2)) {
    printf “%${width}.${precision}f\n”, $value;
}

sqrt
sqrt EXPR
sqrt
이 함수는 EXPR의 제곱근을 계산한다. 만약 EXPR를 생략하면 $_의 제곱근을 반환한다. 세제곱근과 같은 경우에는 ** 연산자를 사용하여 분수 세제곱을 하면 된다.

srand
srand EXPR
이 함수는 rand연산자의 씨앗(seed:일종의 종자)값인 난수를 설정한다. 만약 EXPR을 생략하면 충분히 짐작할 수 있는 형태인 srand(time) 연산을 한다. 따라서, 무작위 패스워드 생성과 같은 보안이 중요한 부분에 대해서는 그런 형태로 사용해서는 안된다. 대신에 다음과 같이 사용한다.
srand( time() ^ ($$ + ($$ << 15)) );
물론 사용자는 중요한 암호화 목적에는 훨씬 더 무작위한 형태의 것이 필요하다. 왜냐하면 현재의 시각을 추정하는 것은 쉬운일이기 때문이다. 1개 이상의 빠르게 변화하는 OS 상태 프로그램의 압축된 출력을 더하는 것이 흔한 방식임을 확인해 보라. 예를 들면:
srand (time ^ $$ ^ unpack “%32L*”, `ps axww | gzip`);
만약 사용자가 자신이 무엇을 하고 있는지 그리고 자신이 왜 그것을 하고 있는지를 정확히 모르고 있다면 사용자 프로그램에서 srand를 여러 번 호출하지 않는다. 이 함수의 핵심은 사용자가 자신의 프로그램을 실행할 때 마다, rand 함수가 매번 다른 순서를 생성하도록 rand함수에게 씨앗을 뿌릴 수 있어야 한다. 단지 사용자 프로그램의 맨 앞에서 한번만 함수를 사용하라, 그렇지 않으면 사용자는 rand 문을 통해 난수를 얻을 수 없다!

stat
stat FILEHANDLE
stat EXPR
이 함수는 FILEHANDLE로 열려진 파일이나 EXPR의 이름을 가진 파일에 관한 통계정보를 13개 요소로 구성된 리스트의 형태로 반환한다. 이 함수는 대개 다음의 형태로 사용된다;
($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize,$blocks)
= stat $filename;
파일 시스템 종류에 따라 지원되는 필드의 수는 달라진다: 다음은 각 필드의 의미를 정리한 것이다.

필드

의미

dev

파일시스템의 디바이스 번호

ino

아이노드(inode) 번호

mode

파일 모드  (종류와 퍼미션)

nlink

파일의 (하드) 링크 번호

uid

파일 소유자의 뉴메릭 사용자 ID

gid

파일 소유자의 뉴메릭 그룹 ID

rdev

디바이스 식별자(특수파일만)

size

바이트 단위의 파일 전체 크기

atime

에포크(epoch)이후 최종 접근 시간

mtime

에포크(epoch)이후 최종 변경 시간

ctime

에포크(epoch)이후 inode 변경 시간(생성 시간이 아님)

blksize

파일시스템 입출력에 대한 선호하는 블록크기

blocks

할당된 실제 블록수

$dev과 $ino를 함께 이용하면 파일을 식별할 수 있다. $blksize와 $blocks은 BSD계열 파일시스템에서만 정의된다. $blocks(정의되어 있다는 전제하에서)은 512 바이트 블록단위로 나타난다. $blocks*512는 $blocks에 그 수가 고려되지 않은 “홀(holes)”나 배정되지 않은 블록을 내포한 파일의 $size와는 크게 다를 수 있음에 유의해야 한다.
만약 stat이 “_”로 구성된 특수 파일핸들에 전달되면 실제 stat(2)는 행해지지 않는다. 그러나, 마지막 stat 혹은 stat에 근거한 파일 시험(-x 연산자)의 stat 구조의 현재 내용이 반환된다.
다음 예제는 먼저 그 변수가 지정하는 파일이 실행파일인지의 여부를 알기 위해 stat $file 기능을 수행한다. 만약 실행파일이면 기존의 stat 구조로부터 디바이스 번호를 가져와 다시 네트웍 파일 시스템(NFS)상의 파일인지의 여부를 다시 시험한다. 그런 파일시스템은 대체로 음수의 디바이스 번호를 가진다.
if (-x $file and ($d) = stat(_) and $d < 0) {
    print “$file is executable NFS file\n”;
}
힌트: 만약 사용자가 그 파일의 크기만을 알고자 하면 -s 파일 시험 연산자를 고려하라. 이 연산자는 직접 바이트 단위의 파일 크기를 반환한다. 그 밖에 파일의 생성 후 지난 시간을 날짜 단위로 반환하는 시험 연산자들도 있다.

study
study SCALAR
study
이 함수는 SCALAR(지정되지 않으면 $_를 사용)가 변형되기 전에 패턴 일치 검색을 예상하여 별도로 SCALAR에 대해 조사한다. 이것은 시간을 절약 할 수도 혹은 그렇지 못할 수도 있다. 즉 사용자가 검색하려는 패턴의 성질 및 그 수와 그리고 검색하려는 문자열내의 글자 빈도의 분포에 따라 달라진다.아마 사용자는 어느쪽이 더 빠른지 알기 위해서 위 연산문을 사용하는 경우와 그렇지 않은 경우의 실행시간을 비교하기를 원할 수 있다. 많은 단문 컨스턴트 문자열(좀 더 복잡한 패턴의 콘스턴트 부분을 포함하여)을 검색하는 그 루프가 가장 많은 이점을 가지고 있다. 만약 사용자의 모든 패턴 일치가 컨스턴트 문자열이고, 맨앞에 고정되어 있으면, 스캐닝(scanning)이 전혀 행해지지 않으므로, study는 전혀 도움이 되지 않는다. 사용자는 한번에 하나의 동작하는 study 문을 가진다. 만약 사용자가 또 다른 스칼라에 대해 study 문을 사용하면, 첫번째는 “study되지 않는” 상태가 된다.
study 문이 동작하는 방법은 다음과 같다: 검색할 문자열 내의 모든 문자로 이루어진 링크드(linked) 리스트가 만들어진다. 따라서, 예를 들면 우리는 글자 “k”가 어디에 위치하는지를 알 수가 있는 것이다. 각 검색 문자열측면에서 보면, C 프로그램과 영어 텍스트로부터 만들어진 고정된 빈도표에 근거하여 가장 희귀한 글자가 선택된다. 이들 가장 희귀한 문자열을 포함하는 부분에 대해서만 검색이 된다. 예를 들면, 다음은 인덱스를 삽입하는 루프의 예이다 – 특정 패턴을 포함한 임의의 행앞에 항목을 생성.
while (<>) {
    study;
    print “.IX foo\n” if /\ebfoo\eb/;
    print “.IX bar\n” if /\ebbar\eb/;
    print “.IX blurfl\n” if /\ebblurfl\eb/;
    …
    print;
}
/\bfoo\b/에 대해 검색하는 경우, “f”가 “o”.보다 희귀하므로 “f”를 포함한 $_의 위치에 대해서만 검색이 이루어 진다. 일반적으로 이것은 병리학상의 경우를 제외하고는 큰 수확이다. 의문점이 하나 있다면, 링크드 리스트를 일단 생성하는데 걸리는 시간보다 그것이 사용자에게 시간을 절약하게끔 해주는지의 여부이다.
프로그램을 실행할 때까지는 알수 없는 문자열을 사용자가 찾아야 한다면, 사용자의 전체 패턴을 항상 다시 컴파일하는 것을 피하기 위해서 전체 루프를 하나의 문자열과 eval 문으로 구축할 수 있다. 전체 파일을 하나의 레코드로 입력하기 위해 $/를 설정하면 fgrep과 같은 특수 프로그램보다 훨씬 빠르게 수행가능 하다. 다음의 예제는 워드 리스트(@words)에 대한 파일 리스트(@files)를 검색하여 일치하는 파일의 이름을 출력한다:
$search = ‘while (<>) { study;’;
foreach $word (@words) {
    $search .= “++\$seen{\$ARGV} if /\\b$word\\b/;\n”;
}
$search .= “}”;
@ARGV = @files;
undef $/; # 전체 파일의 정의를 없애버린다.
eval $search; # this screams
die $@ if $@; # eval 문이 실패한 경우
$/ = “\n”; # 일반 입력 구분자로 돌려놓음
foreach $file (sort keys(%seen)) {
    print $file, “\n”;
}

sub
sub NAME BLOCK
sub NAME
sub BLOCK
sub NAME PROTO BLOCK
sub NAME PROTO
sub PROTO BLOCK
연산자 sub의 사용 형태인 첫번째와 두번째는 실제 연산자의 역할이 아니며, 단지 지명된 서브루틴의 존재를 선언할 뿐이다. 그 이유로 해서 위 2가지 구문에는 NAME을 포함하고 있다(선언문인 경우 반환값이 존재하지 않는다.)
첫번째 사용 형태는 서브루틴의 코드를 내포한 BLOCK을 추가적으로 서브루틴과 함께 정의한다. 두번째 사용 형태는(BLOCK을 사용하지 않은 경우) 단지 직접적인 선언을 하고 있다. 다시 말해 서브루틴에 대한 정의 없이 그 이름만을 선언할 뿐이고, 나중에 서브루틴에 대한 정의를 하는 것이다.(만약 그것이 사용자가 정의한 서브루틴이라는 것을 알면, 해석기(parser)가 단어를 상세하게 처리하므로 이것은 매우 유용한다. 예를들면 사용자는 마치 그것이 리스트 연산자인 것 처럼 서브루틴을 호출할 수 있다.)
실행시 익명의 서브루틴을 생성하는 식에서 사용될 수 있다는 점에서 3번째 형태는 하나의 연산자와 같다. (좀 더 구체적으로는 익명의 서브루틴에 대한 레퍼런스를 반환한다. 왜냐하면, 사용자는 그것에 대한 어떤 종류의 레퍼런스 없이 익명의 것에 관해 이야기 할 수 없기 때문이다.) 만약 익명의 서브루틴이 BLOCK의 밖에서 선언된 어떤 렉시컬 변수(my로 선언된 지역변수)를 참조하면, 그것은 closure로 동작하며, 비록 렉시컬 변수의 원래 영역이 파괴되더라도, 그것은 같은 서브 연산자에 대해 다른 호출 역시 closure 문이 동작 기간동안 의미를 가지는 그런 렉시컬 변수의 정확한 “버전”을 유지하기 위해 필요한 부기(bookkeeping)를 한다..
그 뒤의 3가지 형태는 사용자 서브루틴의 호출이 어떻게 파싱(parsing)되고 분석되는지를 사용자가 명기할 수 있도록 원형(prototype)을 제공한다는 점과 그 결과 사용자의 루틴이 Perl의 내장 함수처럼 동작하도록 만들 수 있다는 점을 제외하고는 앞의 3가지 형태와 동일하다
더 상세한 내용에 대해서는 4장 “익명 서브루틴”과 2장 “서브루틴”을 참고하라.

substr
substr EXPR, OFFSET, LENGTH
substr EXPR, OFFSET
이 함수는 EXPR에 주어진 문자열에서 서브문자열을 추출하여 반환한다. 서브문자열은 문자열의 맨 앞에서 OFFSET 개수의 문자로부터 시작하여 추출된다.(주: 만약 사용자가 $[의 값을 변경했다면, 문자열의 시작은 0에서 부터가 아니며, 만약 그렇지 않다면 문자열의 시작은 0부터이다.] 만약 OFFSET이 음수이면 문자열의 맨 끝에서부터 시작한다. 만약 LENGTH를 사용하지 않으면 문자열 전체가 반환값이 된다. 만약 LENGTH가 음수이면 문자열의 끝에서부터 LENGTH 만큼의 문자가 제거된 나머지가 반환값으로 주어진다. 그렇지 않고 LENGTH가 양수이면 서브문자열의 길이가 된다. 사용자는 substr를 lvalue(어떤 값을 할당할 수 있는 대상)로 사용할 수 있다. 그럴 경우 EXPR은 반드시 유효한 lvalue여야 한다. 만약 사용자가 서브문자열 길이 보다 짧은 것을 할당하려고 하면 그 문자열의 길이는 줄어든다. 앞의 경우와 반대로 길이가 긴 것을 할당하려고 하면 그 문자열을 그 것을 수용할 크기로 늘어난다. 문자열의 길이를 같게 하려면 사용자는 sprintfx 연산자를 이용해 사용자의 값을 늘여주거나 줄여야 한다. 가령 $_의 현재 값에 문자열 “Larry”를 덧붙이려고 하면 :
substr($_, 0, 0) = “Larry”;
$_의 첫번째 문자를 “Moe”로 대치하려면 :
substr($_, 0, 1) = “Moe”;
그리고 $_의 마지막 문자를 “Curly”로 대치하려면:
substr($_, -1, 1) = “Curly”;
앞의 몇가지 예는 $[의 값을 변경하지 않았다는 가정이다.

symlink
symlink OLDFILE, NEWFILE
이 함수는 구 파일이름에 심볼릭 형태로 연결된 새로운 파일이름을 생성한다. 이 함수는 성공인 경우 1을 , 그렇지 않은 경우 0을 반환한다. 심볼릭 링크를 지원하지 않는 시스템에서 이 함수를 실행시 치명적 오류가 발생한다. 이것을 검사하기 위해서는 eval 문을 사용하여 잠재적인 오류가 발생 되는지를 알아본다:
$can_symlink = (eval { symlink(“”, “”); }, $@ eq “”);
혹은 Config 모듈을 사용하라. 사용자가 상대 심볼릭 링크를 제공 가능하지에 대해 주의하라. 왜냐하면 그것은 현재 작업중인 디렉토리가 기준이 아니라, 심볼릭 링크 그 자체의 위치에 대해 상대적으로 해석이 되기 때문이다.
이장에 앞에서 설명된 링크와 readlink를 참고하라

syscall
syscall LIST
이 함수는 리스트의 첫번째 요소로 지정된 시스템 호출을 호출하며, 나머지 요소는 시스템 호출의 인자로 전달한다.(이들중 다수가 POSIX 모듈과 그 밖의 것을 통해 쉽게 이용할 수 있다.) 이 함수는 syscall(2)이 지원되지 않으면 치명적 오류를 발생한다. 인자들은 다음과 같이 해석된다: 만약 인자가 숫자이면 그 인자는 C 언어의 정수처럼 전달된다. 만약 그렇지 않으면 문자열 값에 대한 포인터가 전달된다. 사용자는 그 문자열을 어떤 결과든지 저장될 수 있을 정도로 그 크기가 보장되도록 설정하여야 한다. 만약 그렇지 않은 경우에는 코어덤프(coredump)가 발생한다. 만약 사용자의 정수 인자들이 문자열이 아니고 수치 문의 형태로 해석될 수 없다면 이들 인자에 대해 0을 더하여 그들이 숫자처럼 보이게 해야 한다.(다음 예제를 참고하라.)
다음 예제는 시스템 호출인 setgroups(2)를 이용하여 현재 프로세스의 그룹 리스트에 더한다.(그것은 다중 그룹 멤버쉽을 지원하는 머신에서만 동작한다.)
require ‘syscall.ph’;
syscall &SYS_setgroups, @groups+0, pack(“i*”, @groups);
사용자는 Perl 설치 지시서에 있는 것 처럼 syscall.ph이 존재하기 위해서는 h2ph를 실행해야 한다는 점을 유의해야 한다. 어떤 시스템에서는 그 대신에 “s*”의 pack 템플릿을 필요로 한다. 무엇보다도 syscall 함수는 C 언어에서 제공하는 종류인 int, long, 그리고 char*의 크기가 같다고 가정한다.
syscall을 이식성의 개략적 요소로 생각하지는 말라.

sysopen
sysopen FILEHANDLE, FILENAME, MODE
sysopen FILEHANDLE, FILENAME, MODE, PERMS
이 함수는 FILENAME 명의 파일을 열고 FILEHANDLE과 그 파일을 연계시킨다. 만약 FILEHANDLE이 식이면 그 값은 파일핸들의 이름(혹은 레퍼런스)로 사용된다. 이 함수는 인자인 FILENAME, MODE, PERMS와 함께 open(2)를 호출한다.
MODE 인자의 가능한 값과 플래그 비트는 시스템에 따라 달라진다; 그것들은 Fcntl 라이브러리 모듈을 통해 이용할 수 있다. 그러나, 역사적인 이유로 해서 어떤 값들은 일반적이다: 0은 읽기만 가능한 것을 의미하며, 1은 쓰기만 가능한 것을 그리고 2는 읽고/쓰기 가능함을 의미한다.
만약 FILENAME명의 파일이 존재하지 않고 sysopen 문이 파일을 생성하면(대개 MODE는 O_CREAT 플래그를 포함하기 때문에) PERMS의 값이 새로 생성된 파일의 퍼미션을 지정한다. 만약 PERMS이 생략되었으면 디폴트 값은 0666이 된다. 이 값은 모든 사람이 읽고 쓰기가 가능함을 의미한다. 이 디폴트는 적절하다: umask를 참고하라.
7장에서 설명되는 파일핸들 모듈을 참조하면 sysopen에 대한 객체 지향적 접근방식에 대해 알수 있다. 또한 이 장의 앞에서 설명된 open을 참고하라

sysread
sysread FILEHANDLE, SCALAR, LENGTH, OFFSET
sysread FILEHANDLE, SCALAR, LENGTH
이 함수는 read(2)를 통해 지정된 FILEHANDLE로 부터 LENGTH 바이트의 데이터를 읽어 변수 SCALAR에 저장하고자 한다. 이 함수는 실제 읽은 데이터의 바이트 수를 반환하며, 읽은 데이터가 EOF인 경우 0을 반환한다. 이 함수는 오류 발생시에는 정의되지 않은 값을 반환한다. SCALAR는 읽혀진 데이터의 크기에 맞추어 그 크기가 조절된다. 만약 OFFSET이 명시되었으면 그것은 문자열내에 데이터가 놓여질 위치를 결정하는데 사용된다. 따라서, 버퍼로 사용될 문자열의 중간부터 데이터를 읽을 수도 있다. 그 예는 syswrite를 참고하라. 사용자는 표준 입출력이 일반적으로 취급하는 문제들(인터럽트된 시스템 호출)을 처리할 수 있도록 준비해야 한다. 또한 사용자가 아주 뛰어난 능력의 소유자가 아니라면 같은 파일핸들에다 readsysread를 섞어서 사용하는 것은 피해야 한다.

system
system LIST
이 함수는 시스템상의 프로그램을 실행한다. 이 함수는 exec LIST와 같이 같은 연산을 행하나, 차이점은 이 함수는 맨 먼저 fork를 수행 한 후 실행한다는 것과 exec는 실행시 실행되는 프로그램이 수행완료까지 기다린다는 것이다. 다시 말해(비 유닉스 용어로 말하자면) 이 함수는 사용자가 원하는 프로그램을 실행하며, exec(실행이 성공적이라도 실행 시점으로 돌아오지 않는다)와는 달리 프로그램 실행이 끝나면 실행 시점으로 되돌아 온다. 그리고 exec에서 설명된 것처럼, 인자 처리는 인자의 수에 따라 달라진다는 점을 유의해야 한다. wait(2) 호출에 의해 되돌아 오면서 그 프로그램의 exit status가 반환값이 된다. 실제 출구값(exit value)을 얻기 위해서는 256으로 나누어야 한다.(만약 프로세서가 신호(signal)을 받아서 죽으면 하위 8비트는 1로 설정된다.) exec을 참고하라.
시스템과 백틱(“)은 SIGINT과 SIGQUIT을 막기 때문에, 사용자 프로그램을 실제는 인터럽트할수 없는 신호를 이용하여 실행되는 파일을 죽일 수 있다.
@args = (“command”, “arg1”, “arg2”);
system(@args) == 0
or die “system @args failed: $?”
다음은 신호 및 코어덤프를 포함한 모든 가능성을 조사하기 위하여 유닉스 시스템상에서 system 문이 반환값을 분석하는 좀 더 세련된 예제이다.
$rc = 0xffff & system @args;
printf “system(%s) returned %#04x: “, “@args”, $rc;
if ($rc == 0) {
    print “ran with normal exit\n”;
}
elsif ($rc == 0xff00) {
    print “command failed: $!\n”;
}
elsif ($rc > 0x80) {
    $rc >>= 8;
    print “ran with non-zero exit status $rc\n”;
}
else {
    print “ran with “;
    if ($rc & 0x80) {
        $rc &= ~0x80;
        print “coredump from “;
    }
    print “signal $rc\n”
}
$ok = ($rc != 0);

syswrite
syswrite FILEHANDLE, SCALAR, LENGTH, OFFSET
syswrite FILEHANDLE, SCALAR, LENGTH
이 함수는 변수 SCALARLENGTH 바이트 길이의 데이터를 지정된 FILEHANDLEwrite(2)를 이용해 쓰고자 한다. 이 함수는 실제 쓰여진 데이터의 바이트 수를 반환값으로 주거나, 오류가 발생시에는 정의되지 않은 값을 반환한다. 사용자는 표준 입출력이 일반적으로 처리하는 것, 예를 들면 부분적 write등의 문제를 처리할 수 있도록 준비해야 한다. 사용자가 문자열을 버퍼로 사용하는 경우나 혹은 사용자가 부분적 write로 부터 복구해야 하는 경우에 만약 OFFSET이 지정되어 있으면, 그것은 문자열 내의 write할 위치를 의미한다. 다음은 파일핸들 FROM에서 파일핸들 TO로 데이터를 복사하고자 하는 경우의 예이다:
$blksize = (stat FROM)[11] || 16384; # preferred block size?
while ($len = sysread FROM, $buf, $blksize) {
    if (!defined $len) {
        next if $! =~ /^Interrupted/;
        die “System read error: $!\n”;
    }
    $offset = 0;
    while ($len) { # 부분적 쓰기를 다룸
        $written = syswrite TO, $buf, $len, $offset;
        die “System write error: $!\n”
        unless defined $written;
        $len -= $written;
        $offset += $written;
    };
}
사용자가 아주 뛰어난 능력의 소유자가 아니라면 같은 파일핸들에다(print 혹은 write) 섞어서 호출하고 syswrite를 사용하는 일을 삼가라.

tell
tell FILEHANDLE
tell
이 함수는 FILEHANDLE의 현재 파일 위치(0을 기준으로 바이트 단위로)를 반환한다. 대개는 어느 정도 시간이 지난후 이 값은 seek 함수에 주어진다. FILEHANDLE은 실제 파일핸들의 이름 혹은 파일핸들 객체의 레퍼런스를 결과값으로 주는 식일 수도 있다. 만약 FILEHANDLE이 생략되면 함수는 마지막으로 읽은 파일의 위치를 반환한다. 파일 위치는 정규 파일에 대해서만 의미가 있다. 디바이스, 파이프 그리고 소켓은 파일 위치를 가지지 않는다. 예제는 seek 함수를 참고하라.

telldir
telldir DIRHANDLE
이 함수는 DIRHANDLE에 대한 readdir 루틴의 현재 위치를 알려준다. 이 값은 디렉토리의 특정 위치에 접근하기 위해 seekdir에 주어지는 값이다. 이 함수는 해당 시스템 라이브러리 루틴처럼 가능한 디렉토리 압축에 관한 같은 경고문을 가진다. 이 함수는 readdir이 지원되는 시스템에서 지원되지 않을 수도 있다. 설사 그렇다 하더라도 반환값 생성시 어떤 계산도 필요하지는 않다. 그것은 seekdir에만 의미를 가지는 불명확한 값일 뿐이다.

tie
tie VARIABLE, CLASSNAME, LIST
이 함수는 변수를 구현할 수 있도록 해주는 패키지 클래스에 그 변수를 바인딩(binding)한다. VARIABLE는 연결(tied)되는 변수명이다. CLASSNAM은 적절한 종류의 객체를 구현하는 클래스 명이다. 그 밖의 인자는 새로운 방식의 클래스(TIESCALAR, TIEARRAY, 혹은 TIEHASH를 뜻함)에 전달된다. 대개 이들 인자는 패키지에 따라 달라지는 C 언어의 dbm_open(3) 함수에 전달된다.
“new” 방식에 의해 반환된 객체는 tie 함수에 의해 또 반환되면, 만약 사용자가 CLASSNAME에 속한 다른 방식에 접근하기를 원한다면 그것은 유용하게 사용될 수 있다.(또한, 객체는 tied 함수를 통해 접근할 수 있다.) 그래서, ISAM 구현에 대한 해쉬를 tie 문을 통해 처리하기 위하여 클래스는 한 셋의 키를 순차적으로 처리할 수 있는 별도의 방식을 제공할 수 있다. 왜냐하면, 사용자의 전형적인 DBM 구현은 그것을 할 수 없기 때문이다.
keysvalues와 같은 함수는 DBM 파일처럼 대형 객체에 사용되는 초대형 리스트 값을 반환한다는 점을 유의해야 한다. 사용자는 each 함수를 사용하여 항목을 반복 처리하기를 원할 수도 있다. 예를 들면:
use NDBM_File;
tie %ALIASES, “NDBM_File”, “/etc/aliases”, 1, 0
or die “Can’t open aliases: $!\n”;
while (($key,$val) = each %ALIASES) {
    print $key, ‘ = ‘, $val, “\n”;
}
untie %ALIASES;
해쉬를 구현하는 클래스는 다음의 방법들을 제공해야 한다:
TIEHASH $class, LIST
DESTROY $self
FETCH $self, $key
STORE $self, $key, $value
DELETE $self, $key
EXISTS $self, $key
FIRSTKEY $selfNEXTKEY $self, $lastkey
일반적인 배열을 구현하는 클래스는 다음의 방법들을 제공해야 한다:
TIEARRAY $classname, LIST
DESTROY $self
FETCH $self, $subscript
STORE $self, $subscript, $value
(이책이 쓰여지고 있을때는 다른 방법들이 아직까지 설계되고 있다. 더 필요한 내용은 온행 문서를 참고하라) 스칼라를 구현하는 클래스는 다음의 방법을 제공해야 한다:
TIESCALAR $classname, LIST
DESTROY $self
FETCH $self,
STORE $self, $value
이러한 상세한 방법에 대한 논의는 5장의 “결합 변수의 사용”를 참고하라. dbmopen 과는 달리 함수 tie는 별도의 모듈을 사용하거나 사용자에게 요구하지 않는다. 사용자는 스스로 명시적으로 그렇게 해야한다. 만약 tie 구현에 관해 궁금하며, DB_File과 Config모듈을 참고하라.

tied
tied VARIABLE
이 함수는 VARIABLE하에 있는 객체의 레퍼런스를 반환한다(변수를 패키지에 바인딩한 tie 호출의 결과인 원래의 반환값과 같은 값) 만약 VARIABLE가 패키지에 연결(tied)되지 않으면 함수는 정의되지 않은 값을 반환한다. 따라서, 예를 들면, 현재 사용자 해쉬가 어떤 패키지에 연결(tied)되어 있는지를 알고자 하는 경우 다음과 같이 할 수 있다:
ref tied %hash

time
time
이 함수는 1970년 1월 1일 UTC 부터 시작하여 현재까지의 시간을 윤초가 아닌 초값을 반환한다. 반환값은 gmtimelocaltime의 입력값으로 사용할 수 있으며, stat문의 반환값인 파일 수정과 접근 시간과의 비교시에 이용할 수 있으며 그리고 utime의 입력값으로도 사용할 수 있다. utime의 예제들을 참고하라.

times
times
이 함수는 프로세스 및 그 프로세스의 자식 프로세스들에 대하여 사용자와 시스템의 CPU 시간(단위는 초이며, 초이하 소수점 값까지)을 알려주는 4개의 요소로 구성된 리스트를 반환값한다.
($user, $system, $cuser, $csystem) = times;
예를 들면, Perl 코드의 실행 속도를 측정하고자 하면 다음과 같이 한다:
$start = (times)[0];
$end = (times)[0];
printf “that took %.2f CPU seconds\n”, $end – $start;
tr///
tr///
y///
이 함수는 변환 연산자이며, 유닉스 sed 프로그램과 같은 기능을 한다. 2장을 참고하라

truncate
truncate FILEHANDLE, LENGTH
truncate EXPR, LENGTH
이 함수는 FILEHANDLE로 열려진 파일 혹은 EXPR의 이름을 가진 파일등을 지정된 길이만큼 잘라낸다. 이 함수는 truncate(2) 혹은 비슷한 것이 사용자 시스템에서 지원되지 않으면 치명적 오류가 발생한다.(디스크 공간이 충분하다면, 사용자는 항상 파일의 처음부터 복사하면서 truncate 문을 실행할 수 있다.)

uc
uc EXPR
이 함수는 EXPR(혹은 EXPR이 생략되었으면 $_를 사용)을 대문자로 변환하여 그 결과를 반환한다. 이 함수는 이중인용부호 내의 문자열에 포함된 \U 이스케이프를 구현한 내장함수이다. POSIX setlocale(3) 설정과 관련이 있다.

ucfirst
ucfirst EXPR
이 함수는 EXPR(혹은 EXPR이 명시되지 않으면 $_를 사용)의 첫번째 글자를 대문자로 변환하여 그 결과를 반환한다. 이 함수는 이중인용부호 내의 문자열에 포함된 \u 이스케이프를 구현한 내장함수이다. POSIX setlocale(3) 설정과 관련이 있다. 첫글자를 대문자로 그리고 나머지를 소문자로 만들기 위해서는 다음과 같이 한다:
ucfirst lc $word
위 예는 “\u\L$word”와 동일하다.

umask
umask EXPR
umask
이 함수는 프로세스의 umask 값을 설정하며, 그 전의 값을 반환한다.(umask는 파일 생성시 유닉스가 파일의 퍼미션 비트중 허용하지 않는 것에 대한 정보를 설정하도록 알려준다.) 만약 EXPR을 생략하면 함수는 단지 현재의 umask를 반환한다. 예를 들면, 다른 비트는 1로 설정하고 사용자 비트는 0으로 설정하고자 하면 다음과 같이 한다:
umask((umask() & 077) | 7);

undef
undef EXPR
undef
이 함수는 EXPR의 정의된 값(값은 반드시 lvalue이여야 한다.)을 무효화한다. EXPR은 스칼라 값, 전체 배열 혹은 해쉬 혹은 서브루틴 이름(이름앞에 &를 사용)이어야 한다. 객체와 관련된 스토리지(storage)는 나중에 재 사용이 가능토록 조치된다(대부분의 UNIX 시스템에서는 그 스토리지는 다시 사용이 가능하지는 않다). 함수 undef는 아마 대부분의 특수 변수에 대해서는 사용자가 원하는 결과를 주지 않는다. 그 함수는 항상 정의되지 않은 값을 반환한다. 이 함수를 사용시 EXPR을 생략할 수 있다. 이 때 정의되지 않은 어떤 것도 얻을 수 없다. 그러나, 예를 들어 사용자는 서브루틴의 오류를 통한 반환값으로부터 정의되지 않은 값을 얻을 수 있다. 다음은 undef 문을 일원 연산자로 사용하는 예이다.
undef $foo;
undef $bar{‘blurfl’};
undef @ary;
undef %assoc;
undef &mysub;
인자없이 사용시 undef 문은 값으로 사용가능 하다:
return (wantarray ? () : undef) if $they_blew_it;
select(undef, undef, undef, $naptime);
사용자는 undef를 리스트 할당 좌측의 내정자(placeholder)로 사용할 수 있으며, 그 경우 우측의 해당 값은 단지 버려진다. 앞의 경우를 제외하고는 undef 문을 lvalue로 사용할 수 없다.

unlink
unlink LIST
이 함수는 파일 리스트를 제거한다. 만약 LIST를 생략하면 함수는 $_가 명시하는 파일의 링크를 제거한다. 이 함수는 성공적으로 지워진 파일의 개수를 반환한다. 다음은 명령어 사용 예이다:
$cnt = unlink ‘a’, ‘b’, ‘c’;
unlink @goners;
unlink <*.bak>;
만약 사용자가 수퍼유저이고 -U 플래그가 Perl 구동시 사용되는 경우에만 함수 unlink 는 디렉토리를 지우다는 점을 유의해야 한다. 이러한 조건들이 충족된다 하더라도 사용자 파일시스템상에서 디렉토리의 링크를 제거하는 것은 치명적인 손실을 가져올 수 있음을 알아야 한다. 대신에 rmdir을 사용하라.
다음은 단순한 오류 검사의 기능을 함께하는 rm 명령어의 예이다:
#!/usr/bin/perl
@cannot = grep {not unlink} @ARGV;
die “$0: could not unlink @cannot\n” if @cannot;

unpack
unpack TEMPLATE, EXPR
이 함수는 pack의 반대기능을 한다. 이 함수는 데이터 구조를 나타내는 문자열(EXPR)을 확장하여 리스트 값으로 만들고 그 리스트 값을 반환한다.(스칼라 구문에서 이 함수는 단일 값을 unpack하는데 사용된다.) TEMPLATEpack 함수처럼 똑 같은 포맷을 가진다. 이 함수는 unpack할 값의 순서와 종류를 지정한다.(TEMPLATE에 관한 더 상세한 설명은 pack을 참고하라.)
다음은 좀 느리지만 substr 기능을 하는 서브루틴 예이다:
sub substr {
    my($what, $where, $howmuch) = @_;
    if ($where < 0) {
        $where = -$where;
        return unpack “\e@* X$where a$howmuch”, $what;
    }
    else {
        return unpack “x$where a$howmuch”, $what;
    }
}
그리고 다음의 코드도 있다:
sub signed_ord { unpack “c”, shift }
다음은 완전한 기능을 하는 uudecode 프로그램이다:
#!/usr/bin/perl
$_ = <> until ($mode,$file) = /^begin\es*(\ed*)\es*(\eS*)/;
open(OUT,”> $file”) if $file ne “”;
while (<>) {
    last if /^end/;
   next if /[a-z]/;
    next unless int((((ord() – 32) & 077) + 2) / 3) ==
    int(length() / 4);
    print OUT unpack “u”, $_;
}
chmod oct $mode, $file;
게다가, %number를 사용하여 필드를 앞에 위치하여 사용자가 항목 대신에 항목의 비트 체크섬(chedksum) 값인 그 숫자를 반환값으로 원한다는 것을 나타낼 수 있다. 기본값은 16비트 체크섬이다. 예를 들면, 다음은 시스템 V의 sum 프로그램처럼 수를 합산하는 예이다:
undef $/;
$checksum = unpack (“%32C*”, <>) % 32767;
다음은 비트 벡트의 1로 설정된 비트의 수를 효율적으로 세는 예이다.
$setbits = unpack “%32b*”, $selectmask;
다음은 단순한 MIME 디코드의 예이다:
while (<>) {
    tr#A-Za-z0-9+/##cd; # remove non-base64 chars
    tr#A-Za-z0-9+/# -_#; # uuencode 명려어로 변환된 포맷으로 변환
    $len = pack(“c”, 32 + 0.75*length); # 바이트 길이 계산
    print unpack(“u”, $len . $_); # uudecode와 print
}

unshift
unshift ARRAY, LIST
이 함수는 shift의 반대 기능을 한다.(혹은 사용자가 어떻게 생각하느냐에 따라 push 의 반대 기능으로 생각할 수 있다.) 이 함수는 배열의 맨 앞에 LIST를 붙이고 새롭게 구성된 배열의 요소 개수를 반환한다:
unshift @ARGV, ‘-e’, $cmd unless $ARGV[0] =~ /^-/;

untie
untie VARIABLE
변수와 패키지간의 연결(binding)을 제거한다. tie를 참고하라.

use
use Module LIST
use Module
선언문인 use는 현재의 패키지에 지명된 모듈로부터 시맨틱을 임포트(import)한다. 대개 사용자의 패키지 내에 서브루틴이나 변수 이름을 앨리어스하여 임포트(import) 한다. 이는 다음의 예와 같이 정확히 동일하다.
BEGIN { require Module; import Module LIST; }
BEGIN은 컴파일 할 때 requireimport 문이 사용되도록 요구한다. require 문은 모듈이 메모리에 로딩(loading)되지 않았으면 메모리로 로딩(loading)되도록 한다. import 문은 내장 명령어가 아니다. 그것은 현재 패키지내에 특성 리스트를 임포트(import)하기 위해 Module명의 패키지를 호출하는 일반적인 정적인 방식이다.
비록 대부분의 모듈은 단지 Exporter 모듈내에 정의된 Exporter 클래스로부터 상속(inheritance)을 통한 그 자신의 임포트(import) 방식을 도출하는 것을 결정하지만, 모듈은 자신이 원하는 방식대로 임포트(import) 방식을 구현할 수 있다. 5장을 참조하시오
만약 사용자가 사용자의 이름공간이 변경되지 않기를 원한다면 빈 리스트를 명시적으로 표기해야 한다.
use Module ();
앞의 예는 다음과 동일하다:
BEGIN { require Module; }
이것은 넓게 열린 인터페이스이므로, 프라그마(pragmas: 컴파일러 지시문)는 또한 그렇게 구현된다. 현재 구현된 프라그마는 다음을 포함한다:
use integer;
use diagnostics;
use sigtrap qw(SEGV BUS);
use strict qw(subs vars refs);
이들 가짜모듈은 심볼을 현재의 패키지로 임포트(import)하는 일반적인 모듈과 달리 현재 블록 영역으로 대개 시맨틱을 임포트(import)한다.(후자는 파일의 끝부분에서 효과적이다.) 그리고, 앞의 문과 상응하는 선언문 no가 있으며, use에 의해 임포트(import)되었지만, 나중에 중요하지 않게되어 원래의 모든 의미를 언임포트(unimport)하기 위해 사용한다:
no integer;
no strict ‘refs’;
7장의 표준 모듈 및 프로그램 리스트를 참조하시오

utime
utime LIST
이 함수는 파일 리스트상의 각 파일에 대한 접근 및 수정 시간을 변경한다. 리스트의 첫 2개 요소는 순서대로 수치적인 접근 시간 및 수정 시간의 값이어야 한다. 함수는 성공적으로 변경된 파일의 개수를 반환한다. 각 파일의 inode 변경 시간은 현재 시간으로 설정된다. 다음은 touch 명령어의 사용 예이다:
#!/usr/bin/perl
$now = time;
utime $now, $now, @ARGV;
그리고, 다음은 간단한 오류 검사 기능을 가진 좀 더 세련된 touch 명령어이다:
#!/usr/bin/perl
$now = time;
@cannot = grep {not utime $now, $now, $_} @ARGV;
die “$0: Could not touch @cannot.\n” if @cannot;
표준 touch 명령어는 다음과 같이 실제 잃어버린 파일들을 생성한다:
$now = time;
foreach $file (@ARGV) {
    utime $now, $now, $file
    or open TMP, “>>$file”
    or warn “Couldn’t touch $file: $!\n”;
}
기존 파일에서 시간을 재기 위해서는 stat를 사용한다.

values
values HASH
이 함수는 지명된 해쉬의 모든 값으로 구성된 리스트를 반환한다. 반환되는 값의 순서는 완전히 무작위 순서이나 그 해쉬상에 대해 keys 혹은 each 함수가 생성하는 값의 순서와는 같다. 해쉬를 그 값을 기준으로 정렬하려면 keys의 예제를 참고하라. 아주 큰 DBM 파일에 연결된 해쉬상에 값을 사용하는 것 초대형 리스트를 생성하게 되며, 그 결과 사용자가 초대형 프로세스를 가지게 되고 사용자는 곤경에 빠진다. 어쩌면 사용자는 each 함수를 사용하기를 원할 수도 있다. 그 함수를 이용하면 전체를 하나의 거대한(즉 초대형) 리스트를 만들지 않고 해쉬 항목을 하나씩 처리할 수 있다.

vec
vec EXPR, OFFSET, BITS
이 함수는 문자열(EXPR의 값)을 unsigned 정수의 백터로 취급하며, OFFSETBITS가 지정하는 요소의 값을 반환한다. 또한 이 함수는 특정값이 할당되어 요소의 값이 변경되도록 한다. 이 함수의 용도는 작은 정수 리스트의 저장 영역을 최적화하는 것이다. 정수는 매우 작은값일 수도 있다. 백터는 1비트와 같은 작은 수도 내포할 수 있으며, 그 결과 비트문자열을 생성할 수 있다. OFFSET은 사용자가 원하는 것을 찾기 위해 지나쳐야 할 요소의 개수가 얼마인지를 나타낸다. BITS는 백터를 구성하는 요소당 비트수를 나타내며, 따라서 각 요소는 0..(2**BITS)-1 범위의 값을 가지는 unsigned 정수를 포함한다. BITS는 반드시 1, 2, 4, 8, 16, 혹은 32 중 하나인 값이다.
가능한 많은 수의 요소가 pack 함수를 통해 바이트 단위로 변환이 되며, 그 순서는 vec($vectorstring,0,1)이 문자열의 첫번째 바이트의 가장 하위 비트에 존재하는 것이 보장된다.
요소가 위치할 바이트상의 위치를 알아내려면 사용자는 OFFSET을 바이트필드의 요소의 수로 곱해주어야 한다. BITS가 1이면 바이트 하나당 8개의 요소가 존재한다. BITS가 2이면 바이트 하나당 4개의 요소가 존재한다. BITS가 4이면 바이트 하나당 2개의 요소(니블(nybbles)이라 부른다)가 존재한다. 그런식으로 전개된다.
사용자의 기계가 빅-엔디안(big-endian) 혹은 리틀-엔디안 인지의 여부에 상관없이 vec($foo, 0, 8)는 항상 문자열 $foo의 첫번째 바이트를 참조한다.
vec으로 생성된 비트맵의 예제에 대해서는 select를 참고하라. vec로 생성된 벡터는 논리 연산자 |, &, ^, 그리고 ~를 이용해 처리될 수 있으면, 그리고 피연산자(operands)가 문자열이면 비트 벡터 연산이 행해진다고 가정한다.
비트 벡터(BITS == 1)는 unpack 혹은 pack에 b* 템플릿을 입력하여 1과 0으로 구성된 문자열과 상호 변환이 가능하다. 마찬가지로 니블(BITS==4)벡터는 h* 템플릿을 이용해 변환이 가능하다.

wait
wait
이 함수는 특정 자(child) 프로세스가 종료하기를 기다리며 감소된 프로세스의 pid를 반환값으로 주며, 만약 자 프로세스가 존재하지 않는 경우에는 -1을 반환한다. 상태는 $?에 저장되어 반환된다. 만약 사용자가 좀비 자 프로세스를 가지려 한다면 이 함수나 waitpid를 호출해야 한다. 그런 좀비를 피하기 위한 일반적인 방법은 다음과 같다:
$SIG{CHLD} = sub { wait };
만약 사용자가 자(child) 프로세스를 기대하나 찾지 못했을 경우, 사용자는 system, pipe에 대한 close 문 혹은 fork 문 그리고 wait 문간의 백틱(“)을, 호출 해야 한다. 이들 구성자는 또한 wait(2) 문을 실행하여 사용자의 자 프로세스를 가질 수도 있다. 이들 문제를 피하기 위해서는 waitpid를 사용하라.

waitpid
waitpid PID, FLAGS
이 함수는 특정 자(child) 프로세스가 종료하기를 기다리며, 프로세스가 죽은 경우에는 pid를, 반환값으로 주면, 만약 자 프로세스가 존재하지 않는 경우나 FLAGS가 non-blocking를 지정하며 그 프로세스가 아직 죽지 않은 경우에는 -1을 반환한다. 죽은 프로세스의. 유효한 플래그 값을 얻기 위해서는 다음과 같이 한다:
use POSIX “sys_wait_h”;
시스템 호출인 waitpid(2)wait4(2)가 지원되지 않는 시스템에서는 FLAGS가 단지 0으로 지정될 수 있다. 다시 말해 사용자는 특정 PID를 기다릴 수 있으나, non-blocking 모드에서는 할 수 없다.

wantarray
wantarray
이 함수는 현재 실행되는 서브루틴이 리스트 값을 찾으면 참을 반환한다. 만약 스칼라를 찾으면 거짓을 반환한다. 다음은 실패한 경우의 반환값을 보여주는 전형적인 사용예이다:
return wantarray ? () : undef;
또한 caller도 참고하라. 이 함수는 “wantlist”로 불리워졌으야 했으나 리스트를 배열이라 부를 때와 같이 명칭을 그렇제 정했다.

warn
warn LIST
이 함수는 die 문처럼 STDERR상에 메시지를 생성하나, 프로그램을 종료하거나 예외상황을 발생시키지 않는다.
예를 들면 :
warn “Debug enabled” if $debug;
만약 메시지가 널이면 “무엇인가가 잘못됐다”라는 의미의 메시지로 사용된다.
die 문처럼 개행문자로 끝나지 않는 메시지는 파일과 행수 정보를 자동적으로 뒤에 붙이게 된다. warn 연산자는 -w 스위치와 관련이 없다.

write
write FILEHANDLE
write
이 함수는 지정된 파일핸들에 파일핸들과 관련된 포맷을 사용하여, 정형화된 레코드(가능한 여러행)를 쓴다. 2장의 “포맷” 을 참고하라. 기본값으로 파일핸들의 포맷은 파일핸들처럼 같은 이름을 가진다. 그러나, 파일핸들의 포맷은 다음과 같이 변경이 가능하다.
use FileHandle;
HANDLE->format_name(“NEWNAME”);
Top-of-form processing은 자동적으로 처리된다. 만약 정형화된 레코드를 쓸 현재 페이지에 충분한 공간이 없다면, 다음 장으로 넘어가서 쓰게 된다. 즉 특수 top-of-page 포멧은 새 페이지 헤드를 정형화하는데 사용되며, 그리고 나서 레코드가 씌여진다. 현재 페이지에 남아 있는 행수는 변수 $-에 저장되며, 만약 0이 저장되면 새 페이지에 씌여지는 경우이다.(사용자는 먼저 파일핸들을 선택해야 한다.) 기본값으로 top-of-page 포맷의 이름은 파일핸들 이름에 “_TOP”이 뒤에 붙은 형태이다. 그러나, 파일핸들 포맷을 바꾸고자 하는 경우 다음과 같이 하면 된다.
use FileHandle;
HANDLE->format_top_name(“NEWNAME_TOP”);
만약 FILEHANDLE을 생략하면 출력은 현재의 디폴트 출력 파일핸들인 STDOUT으로 연결된다. 그러나, 이는 select 연산자에 의해 변경이 가능하다. 만약 파일핸들이 하나의 연산식이면, 그 연산식은 실행시 실제 파일핸들을 결정하기 위해 그 식이 평가된다.
writeread의 반대 개념이 아님을 기억하라, 단순히 문자열을 출력하기 위해서는 print를 사용하라, 만약 표준 입출력을 무시하기 위해 이 항목을 찾은 경우라면 syswrite 항목을 참고한다.
만약 사용자가 표준 입출력을 바이패스하기 때문에 이 항목을 검색하고자 한다면 syswrite를 참고하라.
y///
y///
변환 연산자이며, 또한 tr///와 같다. 2장을 참고하라.

 

레퍼런스와 중첩 데이터 구조

실용적이면서 철학적인 이유로 해서 Perl은 항상 평면적이고 선형적인 데이터 구조를 지향해 왔다. 그리고, 그런 점은 여러 가지를 종합해 보면 사용자가 원하는 사항이기도 하다. 그러나, 때때로 사용자는 좀 더 복잡하고 계층적인 형태의 것을 만들기를 원한다. Perl의 구 버전(version)에서는 사용자가 eval 문이나 typeglob 문을 이용하여 간접적인 방식으로 복잡한 데이터 구조를 만들었다.

가령 사용자가 어떤 집단의 사람들 신체에 관련된 자료(가령 나이, 눈 색깔, 몸무게) 통계를 단순한 테이블(2차원 배열) 형태로 만들려고 하는 경우를 가상해 보자

@john = (47, “brown”, 186);
@mary = (23, “hazel”, 128);
@bill = (35, “blue”, 157);

그리고 배열 이름을 요소로 갖는 다른 배열을 만들면 다음과 같다.

@vitals = (‘john’, ‘mary’, ‘bill’);

한편, 앞에서 작성된 테이블을 2차원 데이터 구조로 사용하면 사실상 불편한 점이 많다. 가령 어떤 마을에서 숙박을 한 존(John)의 눈이 충혈되어, 그 내용을 테이블에 반영하고자 하면 다음과 같이 할 수 있다.

$vitals = $vitals[0];
eval “\$${vitals}[1] = ‘red'”;

앞의 프로그램보다 가독성은 떨어지지만 좀더 효율적인 방법은 typeglob 할당문을 사용하여 심볼 테이블의 한 항목을 다른 것에 잠시 앨리어스하는 것이다.

local(*array) = $vitals[0]; # Alias *array to *john.
$array[1] = ‘red’; # $john[1]를 설정

또 다른 방법으로는 병렬 해쉬 배열과 해쉬를 사용한 키 검색을 통한 상징적인 포인터 에뮬레이션(emulation)을 이용하여 심볼 테이블 사용을 가급적 멀리하는 것이다.

마지막 방법으로는 packunpack또는 joinsplit을 이용하여 사용자의 구조를 정의하는 것이다.

포인터와 데이터 구조를 다양한 방법으로 에뮬레이션 할 수 있지만 프로그램 자체가 너무 비대해질 수(unwieldy) 있다. 지금도 Perl은 옛날 방식을 지원하고 있다. 이는 간단한 문제를 해결하는데 그 방식이 유용하기 때문이다. 한편, Perl은 레퍼런스를 지원한다.

레퍼런스는 무엇인가?

eval을 이용한 앞의 예제에서, $vitals[0]은 ‘john’을 값으로 가진다. 즉 그 변수는 다름 변수의 이름인 문자열을 값으로 가지는 것이다. 다시 말해 앞의 변수가 뒤의 변수를 레퍼런스 한다고 할 수 있다. 이는 유닉스 파일시스템에서 심볼릭 링크와 여사한 개념으로 이해해 도 좋다. Perl에서는 심볼릭 레퍼런스를 위한 단순한 메커니즘을 지원하고 있다. 특히 앞의 예제에서 사용한 eval과 typeglob 할당문을 사용할 필요가 없다. 5장의 후반부에 설명되는 “심볼릭 레퍼런스(Symbolic Refernce)”를 참조하시오. 또 다른 종류의 레퍼런스는 하드 레퍼런스이다.

하드 레퍼런스는 다른 변수(어떤 값을 단지 포함하고 있는 통)의 이름을 지칭하는 것이 아니고 실제 그 값 자체를 의미한다. 즉 이는 어떤 내부의 데이터 집합체이며 우리는 사용자의 목구멍뒤로 늘어져 있는 것을 기념하여 그것을 “씽이” 라고 부른다. (만약 사용자가 재미없는 삶을 원한다면 그것을 레퍼런트(지시물)로 지칭해도 좋다.)

예를 들어, 사용자가 변수인 배열에 들어 있는 씽이에 하드 레퍼런스를 생성한다고 가정하자. 이 하드 레퍼런스와 그것이 지칭하는 씽이는 배열 @array가 영역을 벗어나더라도 계속 존재한다.

단지 씽이의 레퍼런스 수가 0이 될 때 씽이가 실제적으로 소멸된다. 이해를 돕기 위해 다르게 설명하면 Perl 변수는 심볼 테이블 안에 존재하며 하나의 하드 레퍼런스를 가진다. 이 하드 레퍼런스는 변수의 어떤 존재(숫자와 같은 씽이나 배열 혹은 해쉬와 같은 복잡한 씽이이며 하나의 레퍼런스가 이들 변수가 가지고 있는 값을 지칭한다)를 지칭하는 것이다.

같은 씽이를 지칭하는 서로 다른 하드 레퍼런스가 존재할 수 있으나, 변수는 그 점에 대해 알지 못한다. 심볼릭 레퍼런스는 다는 변수의 이름을 지칭할 뿐이어서 항상 관련된 이름의 위치가 존재한다. 그런데 반해 하드 레퍼런스는 단지 씽이를 가리킬 뿐이다.

씽이에 대한 다른 레퍼런스가 존재하는지 혹은 그 레퍼런스들중 어느 것이 변수로부터 나왔는지 대해 알수 없다(혹은 개의치 아니한다). 따라서, 하드 레퍼런스는 익명의 씽이 를 참조할 수 있다.

그러한 모든 익명의 씽이는 하드 레퍼런스를 통해 접근할 수 있다. 그러나, 그 역은 반드시 성립하지는 않는다. 단지 어떤 것에 대한 하드 레퍼런스를 가졌다고 해서 반드시 그것이 익명의 것이라고 할 수는 없다. 즉 어떤 이름을 가진 변수의 또 다른 레퍼런스가 있을 수 있는 것이다.(만약 그것이 typeglob을 통해 앨리어스 되어 있으면, 그것은 2개 이상의 이름을 가질 수 있는 것이다.)

5장의 용어를 사용해서 표현하자면, 변수를 레퍼런스 한다는 것은 씽이의 변수에 대한 하드 레퍼런스를 생성하는 것이다. (이러한 창조적인 행위를 하는 특별할 연산자가 존재한다). 그렇게 생성된 하드 레퍼런스는 단지 스칼라 값을 가지며 다른 스칼라 값들과 같이 아주 비슷한 성격을 가진다.

이 스칼라 값을 참조하는 것은 그것을 통해 원래의 씽이를 지칭하는 것이다. 이는 씽이를 읽거나 쓸 경우에 사용자가 해야 할 작업이다. 레퍼런스와 레퍼런스 참조는 특정 방식(mechanism)을 명시적으로 내포할 때 사용된다. Perl에서는 암묵적인 레퍼런스와 참조는 존재하지 않는다.

모든 스칼라는 하드 레퍼런스를 가지며, 그 레퍼런스는 데이터 구조를 가리킨다. 배열과 해쉬는 스칼라 값을 가지므로 사용자는 배열의 배열, 해쉬의 배열, 배열의 해쉬, 해쉬와 함수의 배열등을 생성할 수 있다.

Perl 배열과 해쉬는 내부 구조적으로는 1차원임을 기억해야 한다. 즉 이들은 스칼라 값(문자열, 숫자, 레퍼런스)만을 가진다. “변수의 변수”와 같은 구문을 사용시 우리는 실제 배열을 레퍼런싱하는 배열을 의미하는 것이다. 그러나, Perl을 이용하여 배열의 배열을 실제 구현하는 유일한 방법이므로 더 짧고, 덜 정확한 구문이 거짓이 될 정도로 부정확한 것은 아니며, 만약 사용자가 고집하지 않는다면 이는 완전히 틀렸다고 부정될 수 없는 것이다.

하드 레퍼런스의 생성

레퍼런스를 작성하는 것은 여러가지 방법이 있으며, 그 결과인 레퍼런스를 어떻게 사용하는가에 대해 설명하기 전에, 생성 방법에 대해 설명한다.

역슬래쉬(Backslash) 연산자

사용자는 단일 역슬레쉬 연산자를 이용하여 명칭이 있는 변수나 서브루틴을 생성할 수 있다. (사용자는 익명의 스칼라 값에 대해 사용할 수 있다.) 이것은 C 언어에서 & 연산자(주소 연산자)와 같은 기능을 한다.

다음은 그 사용예이다:

$scalarref = \$foo;
$constref = \186_282.42;
$arrayref = \@ARGV;
$hashref = \%ENV;
$coderef = \&handler;
$globref = \*STDOUT;

익명의 배열 생성기

사용자는 대괄호를 이용하여 익명의 배열에 대한 레퍼런스를 생성할 수 있다.

$arrayref = [1, 2, [‘a’, ‘b’, ‘c’]];

앞의 예에서는 3개의 원소로 구성된 익명의 배열에 대한 레퍼런스를 구성했다. 이중 3번째 원소는 또 다른 3개의 원소로 구성된 익명의 배열을 가리키는 레퍼런스이다.

이들 대괄호는 Perl 해석기(parser)가 식에서 어떤 항목이 존재하기를 바라는 곳에서만 앞의 경우처럼 정상 동작을 하며, 배열의 특정 값을 지칭하고자 할 때 사용하는 대괄호(배열의 인덱스를 표시할 때 사용하는 괄호)와는 혼동해서는 안된다.(비록 배열에 명확하게 기호화된 조합이 있을지라도)

인용 부호내의 문자열에 존재하는 대괄호는 익명의 배열에 대한 레퍼런스를 치환 하지 않는다. 오히려, 괄호는 문자열의 문자 원소로 취급된다. (그러나, 만약 사용자가 문자열 내에서 치환하고자 하고, 그 치환을 정의하는 식이 괄호를 포함하면 그 식 내부에서 일반적인 의미를 지니게 된다. 왜냐하면 결국에는 그들 전부가 하나의 식이 되기 때문이다.)

열거된 리스트에 대한 레퍼런스를 생성하는 것은 괄호를 사용하는 것과 같지 않다는 점을 주의해야 한다. 대신에 레퍼런스 리스트를 생성하는 하나의 빠른 방법인 것이다.

@list = (\$a, \$b, \$c);
@list = \ ($a, $b, $c); # 동일한 결과

익명의 해쉬 컴포저(Composer)

사용자는 {}를 사용하여 익명의 해쉬에 대한 레퍼런스를 생성할 수 있다:

$hashref = {
    ‘Adam’ => ‘Eve’,
    ‘Clyde’ => ‘Bonnie’,
};

위 값은 글자 문자열이다; 변수와 식은 역시 값으로 사용될 수 있다. 또한 해쉬의 값들(키 값은 아님)에 대해 사용자는 익명의 해쉬와 배열 생성기를 원하는 대로 섞어서 사용하여 사용자가 원하는 대로 복잡한 구조를 생성할 수 있다. 이들 {}는 Perl 해석기(parser)가 식에서 어떤 용어가 존재하여야 하는 경우에 동작하는 것이며, 해쉬의 특정 값을 지칭하고자 할 때 사용하는 대괄호(해쉬의 인덱스를 표시할 때 사용하는 괄호)와는 혼동하지 말라.(비록 배열에 기호화된 명확한 관계가 존재해도.)

인용부호로 싸인 문자열내의 {}는 익명의 해쉬에 대한 레퍼런스의 치환을 하지 않는다. 오히려, {}는 문자열의 문자 항목으로 해석된다.(그러나, 치환식에 관한 경고는 괄호에 적용되는 것처럼 같이 적용된다.)

{}는 블록 및 여러 개의 것을 묶는데 사용되므로, 사용자는 때로는 문장(statement)의 시작부분에 +나 return을 두어 Perl이 ‘{‘가 블록을 시작하는 것으로 해석하지 않도록 한다.

예를 들어, 만약 사용자가 새로운 해쉬를 생성하며, 그에 대한 레퍼런스를 반환하는 함수를 원한다면 다음과 같은 선택사양을 가질 수 있다:

sub hashem { { @_ } } # 틀렸지만 경고하지 않음
sub hashem { +{ @_ } } # 옮음
sub hashem { return { @_ } } # 옮음

익명의 서브루틴 컴포저

사용자는 서브루틴 이름을 사용하지 않으면서 sub를 사용하여 익명의 서브루틴에 대한 레퍼런스를 생성할 수 있다.

$coderef = sub { print “Boink!\n” };

여기서 세미콜론이 사용되고 있음을 유의해야 한다. 세미콜론은 하나의 식이 끝남을 나타내기 위해서 필요하다.(서브루틴의 이름을 선언한 후에는 세미콜론은 필요하지 않다.) 이름이 표기되지 않은 sub {}는 연산자처럼 가령 do {} 혹은 eval {}처럼 하나의 선언문은 아니다. 다시 말해 그것은 코드에 대한 레퍼런스를 생성하여 반환값으로 돌려주는 것이다. 그러나, 앞의 예제를 몇 번이고 실행하더라도 $coderef 는 같은 익명의 서브루틴을 참조(refer)할 것이다.

객체 생성자

서브루틴은 레퍼런스를 반환값으로 사용할 수 있다. 그것은 마치 진부한 얘기로 들릴 수 있다. 가끔 사용자는 레퍼런스 자체를 생성하기 보다는, 서브루틴을 사용하여 레퍼런스를 생성하고자 하는 경우도 있다. 특히 특수한 서브루틴인 생성자는 오브젝트에 대한 레퍼런스를 반환값으로 준다. 하나의 객체는 씽이가 어떤 종류의 클래스와 관련되어 있는지를 알고 있는 하나의 특수한 종류의 씽이인 것이다. 생성자는 그 관계를 생성하는 방법을 안다. 즉 생성자는 일반적인 씽이를 가지고 하나의 객체로 변환하여 관계를 생성하는 것이다.(이때 오브젝트는 하나의 오브젝트이면서 씽이인 것이다.) 생성자가 이러한 일을 해 내기 위해 사용하는 연산자를 bless라 칭한다. 그래서 우리는 하나의 객체를 blessed 씽이라 부른다. 생성자는 관례적으로 new() 라고 표기한다. 그러나, 반드시 그렇게 표기해야 하는 것은 아니다. 생성자는 대개 다음 2가지 중 한가지의 형태로 호출된다.

$objref = new Doggie Tail => ‘short’, Ears => ‘long’;
$objref = Doggie->new(Tail => ‘short’, Ears => ‘long’);

Perl 오브젝트인 패키지, 모듈 그리고 객체 클래스에 관한 논의는 5장을 참조하라.

파일핸들 레퍼런스

파일핸들 레퍼런스는 typeglob 레퍼런스를 통해 생성 가능하다. 이 방법은 이름을 가진 파일핸들을 서브루틴으로 넘기거나 받아오는 경우 또는 대형 데이터 구조에 저장하는 가장 좋은 방법이다.

splutter(\*STDOUT);
sub splutter {
    my $fh = shift;
   print $fh “her um well a hmmm\n”;
}
$rec = get_rec(\*STDIN);
sub get_rec {
    my $fh = shift;
    return scalar <$fh>;
}

그러나, 사용자가 기존의 이름을 가진 파일핸들을 참조할 필요가 없다면, 사용자는 새로운 객체 지향적인 라이브러리 모듈중 하나를 사용하는 것을 고려해야 한다. 이들 모듈은 구성자를 사용해 파일핸들 오브젝트를 제공한다(앞의 부분을 참고하라). 어떤 경우든 사용자는 직접 파일핸들 이름을 사용할 필요가 없고, 파일핸들로 해석될(여러가지로 해석이 되는) 그 무엇인가에 대한 레퍼런스를 저장할 스칼라를 사용하면 된다. 앞에서도 잠깐 얘기했듯이 여기에서도 신기한 레퍼런스 참조가 암묵적으로 사용되고 있다.

암묵적인 레퍼런스 생성

레퍼런스를 생성하는 마지막 방법은 실제 하나의 방법은 아니다. 적절한 형식의 레퍼런스는 사용자가 존재할 것이라는 가정을 하는 구문 내에서 그들을 참조하면 적절할 형식의 레퍼런스가 존재하게 된다. 이것은 대단히 유용하며 사용자가 원했던 것이기도 하다. 이 주제는 뒷 부분에서 설명될 것이다.

하드 레퍼런스 사용하기

레퍼런스를 생성하는 방법이 여러 가지인 것처럼, 레퍼런스 참조나 레퍼런스를 사용하는 방법에도 여러 가지가 있다.

변수를 변수 이름으로 사용하기

일반적으로 변수나 서브루틴 이름의 한 부분으로 어디에나 식별자(identifier)를 사용할 수 있으나, 사용자는 그 식별자를 정확한 형식의 레퍼런스를 포함하는 단순한 스칼라 변수로 대치할 수 있다.

예를 들면:
$foo = “two humps”;
$scalarref = \$foo;
$camel_model = $$scalarref; # $camel_model에는 “two humps”가 저장됨

다음은 다양한 레퍼런스 참조의 예이다:

$bar = $$scalarref;
push(@$arrayref, $filename);
$$arrayref[0] = “January”;
$$hashref{“KEY”} = “VALUE”;
&$coderef(1,2,3);
print $globref “output\n”;

엄밀히 말해서 우리는 앞의 예제에서 $arrayref[0]나 $hashref{“KEY”}를 참조 하지 않는다는 점을 이해해야 한다. 스칼라 변수의 참조는 배열이나 해쉬의 검색 전에 행해진다. 단순한 스칼라 변수보다 더 복잡한 것을 참조하기 위해 사용자는 다음에 설명되는 2가지 방법중 하나를 사용해야 한다. 그러나, “단순 스칼라”는 다음에 설명되는 첫번째 방법을 재귀적으로 사용하는 하나의 식별자를 내포할 수 있다. 그 결과 다음 예제는 “howdy”를 출력하게 된다.

$refrefref = \\\”howdy”;
print $$$$refrefref;

‘$’ 사인은 오른쪽에서 왼쪽으로 실행된다고 보면 된다.

BLOCK을 변수이름으로 사용하기

두번째 방법은 변수 대신에 블록(BLOCK)을 사용하는 것 이외에는 첫번째와 같다. 변수나 서브루틴 이름의 한 부분으로 어디에나 식별자(identifier)를 사용할 수 있으나 사용자는 그 식별자를 정확한 형식의 레퍼런스를 반환하는 블록으로 대치할 수 있다. 다시 말해 앞의 예제들은 다음과 같이 처리 될 수도 있다.

$bar = ${$scalarref};
push(@{$arrayref}, $filename);
${$arrayref}[0] = “January”;
${$hashref}{“KEY”} = “VALUE”;
&{$coderef}(1,2,3);

사실 이런 단순한 예에서는 중괄호를 사용하는 것은 불필요한 일이다. 그러나, 블록은 임의의 식을 내포할 수 있다. 특히 또 다른 스크립트를 포함한 식을 내포할 수 있다. 다음 예제에서, $dispatch{$index}는 서브루틴에 대한 레퍼런스를 포함한다고 가정한다. 이 예제에서는 3개의 인자를 가진 서브루틴을 실행하는 것이다.

&{ $dispatch{$index} }(1, 2, 3);

화살표 연산자 사용

배열이나 해쉬의 레퍼런스에 대한 레퍼런스를 참조하는 세번째 방법은 중간 삽입 연산자인 ->를 사용하는 것이다. 이것은 각각의 배열이나 해쉬의 요소를 쉽게 접근할 수 있도록 해 주는 문법적인 첨가제의 하나이다. 특히 레퍼런스 식이 복잡한 경우에는 더욱 그러하다. 앞에서 이미 소개했듯이 이 3가지 방법에 대한 각각의 표기 방법은 다음과 같다.(판독성을 위해 3가지 표기법에 빈 공간을 넣었다.)

$ $arrayref [0] = “January”; #1
${ $arrayref }[0] = “January”; #2
$arrayref->[0] = “January”; #3
$ $hashref {KEY} = “F#major”; #1
${ $hashref }{KEY} = “F#major”; #2
$hashref->{KEY} = “F#major”; #3

이 예제에서 3번째 표기법으로 나타난 식에는 첫번째 $가 사용되지 않았음을 볼 수 있다. 그러나, 그것은 슬라이스가 아니라 스칼라 값을 레퍼런스 한다는 점이 암묵적으로 표시 되어 있으며, 꼭 그 용도로만 사용이 가능하다. 그러나, 2번째 표기법처럼 사용자는 -> 연산자 왼쪽에 다른 레퍼런스 참조뿐만 아니라 어떤 식이든지 사용 가능하다. 이는 화살표 연산자는 왼쪽에서 오른쪽으로 연관지어 나가기 때문이다:

print $array[3]->{“English”}->[0];

$array[3]와 $array->[3]는 같지 않음을 유의해야 한다. 전자는 배열 @array의 4번째 원소이며, 후자는 변수 $array에 배열(익명 배열도 가능)의 4번째 원소에 대한 레퍼런스 값이 저장된 경우이다. 가령 $array[3]이 정의되지 않았다고 가정할 때, 다음의 문장은 여전히 유효하다:

$array[3]->{“English”}->[0] = “January”;

앞의 예는 레퍼런스가 lvalue 문에 사용될 때 자동적으로 존재하게 된다는 것으로 앞에서 이미 언급한 몇가지 경우 중 하나이다. 가령 $array[3]가 정의되어 있지 않다고 가정하면, 자동적으로 해쉬 레퍼런스로 정의되며 그 결과 {“English”}를 검색할 수 있다.

일단 그것은 완료되었으며, $array[3]->{“English”}는 [0]을 검색할 수 있는 배열 레퍼런스를 이용해 자동적으로 정의된다. 그러나, 이것은 요소를 생성하고자 할 때만 가능하다. 만약 사용자가 그 값을 출력하고자 하면 아무것도 얻을 수는 없다. 사용자는 단지 정의되지 않은 값만을 얻게 된다. 좀 더 단순한 방법을 하나 더 소개한다. 중괄호나 대괄호를 싼 서브스크립트(subscripts) 사이의 화살표 연산자는 선택사양이다. 따라서, 사용자는 앞의 코드를 다음과 같이 작성할 수도 있다:

$array[3]{“English”}[0] = “January”;

일반적인 배열의 경우에서, 앞의 코드는 C 언어의 배열처럼 다차원 배열을 사용자에게 준다:

$answer[$x][$y][$z] += 42;

대체로 만족할 만하지만 C 언어의 배열처럼 전적으로 만족할 만한 것은 아니다. 예를 들어 C 언어는 요구가 있는 경우 배열을 늘일 방법이 없는 반면에 Perl은 가능하다. 또한, 해석(parsing) 방법이 다른 두 언어는 비슷한 구조(constructs)가 존재한다. Perl에서 다음 두 문은 동일하다:

$listref->[2][2] = “hello”; # 꽤 명확하다.

$$listref[2][2] = “hello”; # 약간 이해하기가 어렵다

*a[i]가 “a의 i번째 요소가 가르키는 것”을 의미하는 것으로 이해하는 C 프로그래머는 앞의 두번째 문을 보면 당황할 수 있다. 그러나, Perl에서는 5개의 참조 접두어($ @ * % &)가 효과적으로 서브스크립팅(subscripting) 중괄호 {} 혹은 대괄호 [][2] 보다 훨씬 강하게 바인딩(binding)한다. 따라서, 배열의 레퍼런스는 $listref[$I]이 아니라 $$listref이다. 만약 사용자가 C 언어의 개념을 원하면 ${$listref[$i]}를 사용하여 맨앞의 $ 참조보다 앞서서 $listref[$i]가 처리되도록 하던지 혹은 -> 개념을 사용해야 한다

$listref[$i]->[$j] = “hello”;

오브젝트 메소드 사용

만약 레퍼런스가 객체(즉 blessed 씽이)의 레퍼런스라면, 그 객체의 내부에 접근할 수 있는 방법이 아마 존재할 것이다. 그리고, 그 객체의 접근방법을 정의하는 클래스 패키지를 작성하지 않는 한, 사용자는 아마 그 접근 방법을 고수해야 한다.(그러한 패키지를 사용하면 원할 때 객체를 단지 씽이로 취급하여 처리할 수 있다.)

다시 말해 충실하고, 정말 타당성 있는 이유가 없이는 객체의 캡슐화를 어기지는 말라. Perl은 캡슐화를 강요하지는 않는다. 우리는 전체주의자가 아니다. 그렇지만 어느 정도의 기본적인 정도는 지켜주길 바란다.

하드 레퍼런스를 이용한 트릭

사용자는 레퍼런스가 가르키는 씽이의 종류가 무엇인지를 알기 위해 ref 연산자를 사용할 수 있다. 사용자는 ref 연산자를 인자가 레퍼런스인 경우는 참값을, 그렇지 않은 경우에는 거짓값을 반환하는 “typeof” 연산자로 생각해도 좋다. 반환값은 레퍼런스되는 것의 종류에 따라 달라진다.

내장형은 다음과 같다:
REF
SCALAR
ARRAY
HASH
CODE
GLOB

만약 사용자가 단순히 문자열로 된 문장에서 하드 레퍼런스를 사용하면 그것은 종류와 주소를 내포함 문자열로 변환된다: SCALAR(0x1fc0e). (레퍼런스 카운트(count) 정보는 잃어버리기 때문에 역변환은 불가능하다.) 사용자는 레퍼런스된 씽이와 객체 클래스로 동작하는 패키지를 연관시키기 위해 bless 연산자를 사용할 수 있다. 그렇게 할 경우 ref는 내부 종류 대신에 패키지 명을 반환한다. 문자열로 된 문장에서 사용되는 객체 레퍼런스는 주소와 함께 외부와 내부 종류를 포함하는 문자열을 반환한다: MyType=HASH(0x20d10). 객체에 관한 더 상세한 내용은 5장을 참고하라. 참조 문법은 항상 원하는 레퍼런스의 종류를 나타내기 때문에, 비록 typeglob이 다양한 종류의 다수의 씽이를 포함하더라도, typeglob은 레퍼런스와 같은 방식으로 사용될 수 있다. 따라서, ${*foo}와 ${\$foo}는 같은 스칼라 변수를 참고한다. 후자는 좀 더 효율적이다. 다음은 문자열을 호출하는 서브루틴 호출의 값을 치환하는 트릭이다:

print “My sub returned @{[ mysub(1,2,3) ]} that time.\n”;

그것은 다음과 같이 동작한다. 컴파일시에 @{…}는 이중 인용부호에 싸인 문자열에서 볼 수 있다. 그것은 레퍼런스를 반환하는 블록으로 파싱(parsing)된다. 블록내에서는 대괄호 내에 존재하는 것으로부터 익명의 배열에 대한 레퍼런스를 생성하는 대괄호가 존재한다. 따라서, 실행시 mysub(1,2,3)가 호출되고 그 결과는 익명의 배열에 로딩(loading)되며, 그 배열에 대한 레퍼런스는 블록내에서 반환된다. 그리고나서, 곧 그 배열 레퍼런스는 둘러싼 @{…}에 의해 참조된다. 그리고, 배열 값은 이중 인용부호로 싸인 문자열안으로 일반 배열처럼 치환된다. 다음과 같이 그 궤변은 임의의 식에 대해서도 유용하다:

print “That yields @{[ $n + 5 ]} widgets\n”;

그렇지만 조심하라. 대괄호 안은 리스트 구문을 식에 제공한다. 비록 앞의 예에서 mysub()에 대한 호출이 약간 염려되지만, 이 경우 그것은 문제가 되지 않는다. 그것이 문제가 되는 경우에는 비슷한 트릭을 스칼라 레퍼런스와 함께 사용할 수 있다. 그것은 썩 내키는 것은 아니다.

print “That yields ${ \($n + 5) } widgets.”;

클로우저(Closures)

앞서 우리는 익명의 Sub{}를 이용하여 익명의 서브루틴의 생성에 관해 이야기했다. 익명의 서브루틴은 사용자 코드내의 어떤 위치에 생성되므로(어떤 변수에 저장할 레퍼런스를 생성하기 위해), 그런 루틴들은 실행시에만 존재하는 것으로 여겨진다.(즉 그 루틴들은 정의되는 위치뿐만 아니라 생성의 시간이 별도로 정해져 있는 것이다.) 이런 사실 때문에 익명의 서브루틴은 my 변수에 관해서는 closures로 동작한다. 즉 현재 영역내에 문법적으로 지역변수로 보여지는 변수에 관하여. Closure는 리스프 세계에서 나온 개념이며, 만약 사용자가 특정 시간에 특정 렉시컬 구문에 익명의 함수를 정의하면, 그것은 문맥의 바깥에서 호출되더라도 문맥내에서 실행되는 것처럼 동작한다. 다시 말해 같은 렉시컬 변수의 많은 다른 인스턴스(instances)가 그 전이나 그 후에 생성된다 하더라도, 사용자는 렉시컬 변수의 동일한 복사본을 얻을 수 있다. 사용자가 그것을 호출할 뿐만 아니라 정의할 때, 이것은 사용자로 하여금 서브루틴에 인자를 전달하는 방법을 제공한다. 콜백(callbacks)과 같은 나중에 실행하는 작은 크기의 코드를 작성하는데 유용하다. 사용자는 closureseval 문을 사용하지 않고 서브루틴 템플릿을 작성하는 하나의 도구로 생각할 수도 있다. 렉시컬 변수는 템플릿을 채워야 하는 인자와 같다. 다음은 closures가 어떻게 동작하는 지를 보여주는 작은 예제이다.

sub newprint {
    my $x = shift;
    return sub { my $y = shift; print “$x, $y!\n”; };
}
$h = newprint(“Howdy”);
$g = newprint(“Greetings”);
# Time passes…
&$h(“world”);
&$g(“earthlings”);

위 코드는 다음을 출력한다:

Howdy, world!
Greetings, earthlings!

익명의 서브루틴이 실행될 때, my $x가 외견상으로는 그 영역을 벗어나는 것처럼 보이지만, $x가 newprint()에 전달되는 값을 어떻게 계속해서 참고할 수 있는지에 대해서는 특히 유의해야 한다. 바로 그 점이 closures가 무엇인가를 나타내 주는 것이다. 이 방법만이 my 변수에 적용된다. 전역변수는 기존의 동작 방식으로 동작한다(전역변수는 렉시컬 변수처럼 생성 혹은 파괴되지 않기 때문이다). 대체로 closures는 사용자를 곤란에 빠뜨릴 그런 것은 아니다. 사용자가 closures를 필요로 하면, 그것은 사용자가 원하는 것을 처리할 뿐이다.

Perl은 C++이 하는 것처럼 멤버 포인터를 제공하지 않는다. 그러나, 사용자는 closure를 사용하여 비슷한 효과를 얻을 수 있다. 가령 사용자는 특정 객체에 대한 방식에 대한 포인터를 원한다고 가정하자. 사용자는 렉시컬 변수가 closure에 연결(binding)되는 것처럼 객체와 방식 둘 다를 기억할 수 있다:

sub get_method_ref {
    my ($self, $method) = @_;
    return sub { return $self->$method(@_) };
}
$dog_wag = get_method_ref($dog, ‘wag’);
&$dog_wag(“tail”); # Calls $dog->wag(‘tail’).

심볼릭 레퍼런스

만약 사용자가 하드 레퍼런스가 아닌 값을 디레퍼런스하고자 하면 어떤 일이 벌어질까? 그 값은 심볼릭 레퍼런스로 처리된다. 즉 레퍼런스(여전히 스칼라 값을 가진다)는 문자열로 해석된다. 문자열은 씽이(가능한 익명의)에 대한 직접적인 링크보다 변수의 이름으로 사용된다. 다음은 어떻게 동작하는 지를 보여준다:

$name = “bam”;
$$name = 1; # $bam을 설정
${$name} = 2; # $bam0을 설정
${$name x 2} = 3; # $bambam을 설정
$name->[0] = 4; # $bam[0] 을 설정
@$name = (); # @bam의 값을 지움
&$name(); # &bam()를 호출 (구버전 Perl처럼)
$pkg = “THAT”; # (“package” 혹은 “pack”를 사용하지 말라!)
${“${pkg}::$name”} = 5; # eval 문 없이 $THAT::bam를 설정

이것은 매우 강력한 기능이며, 하드 레퍼런스를 사용하는 것처럼 동작한다는 점과 그 대신에 심볼릭 레퍼런스를 사용한다는 점에서 약간 위험한 면도 있다. 그런 점을 막기위해 다음과 같이 할수 있다:

use strict ‘refs’;

그리고, 하드 레퍼런스만이 에워싼 블록의 나머지 부분에 대해서 허용된다. 내부 블록은 그 제약을 다음과 같이 피할 수 있다:

no strict ‘refs’;

다음 2행으로 구성된 코드사이의 차이점을 인지하는 것은 중요하다:

${identifier}; # $identifier와 동일
${“identifier”}; # $identifier와 동일하지만 심볼릭 레퍼런스로 처리됨

2번째 형태는 심볼릭 레퍼런스로 처리되기 때문에, 그것은 use strict ‘refs’ 사용시에 오류를 발생한다. 패키지 변수만이 심볼릭 레퍼런스에 보여진다. 렉시컬 변수(my로 선언된다)는 패키지 심볼 테이블에 존재하지 않으며 따라서 이 매카니즘에는 보이지 않는다. 예를 들면:

local $value = “10”;
{
my $value = “20”;
print ${“value”};
}

위 코드는 “20”이 아니라 “10”을 출력한다. local 문은 패키지 변수에만 영향을 미친다는 것을 기억하라. 이때 패키지 변수는 패키지에서 전연 변수로 동작한다.

중괄호, 대괄호, 인용부호

앞의 부분에서 우리는 ${identifier}가 심볼릭 레퍼런스로 처리되지 않는다는 점을 지적했다. 이제 사용자는 이것이 예약어의 어떻게 상호 동작하는지 궁금할 것이다. 짧은 답변을 원한다면 그것은 상호 동작하지 않는다는 것이다. push 문이 예약어 이지만 다음 2개의 문은 “pop on over”를 출력한다:

$push = “pop on “;
print “${push}over”;

그 이유는 다음과 같다. 역사적으로 중괄호의 사용을 통해 유닉스 쉘이 어떻게 변수 명을 일련의 문자 숫자 텍스트로부터 분리했는가를 나타낸다. 그것은 많은 이들이 기대하는 변수 치환의 동작 형태이다. 그래서, 우리는 Perl에서도 같은 방식으로 동작하도록 만들었다. 그러나, Perl의 경우 그 개념은 더욱 확대되어 레퍼런스를 생성하는데 사용되는 중괄호에도 적용된다. 이때 중괄호는 인용 부호에 존재할 수도, 하지 않을 수도 있다.

print ${push} . ‘over’;

혹은 심지어:

print ${ push } . ‘over’;

앞의 2개의 문은 “pop on over”을 출력한다. 이 경우 비록 중괄호가 이중 인용부호 바깥에 있다 하더라도 그 결과는 동일하다. 같은 규칙이 해쉬를 서술하는데 사용되는 모든 식별자에 적용된다. 따라서, 다음과 같이 작성하는 것 보다:

$hash{ “aaa” }{ “bbb” }{ “ccc” }

다음과 같이 작성하는 것이 좋다.

$hash{ aaa }{ bbb }{ ccc }

그리고, 그 예약어를 서술하는지의 여부는 개의치 않는다. 아주 드문 경우에 사용자는 다음과 같이 하고자 할 수 있다:

$hash{ shift }

사용자는 그 무엇인가를 추가하여 단순한 식별자가 아닌 그 이상으로 만들어, 예약어로 해석 되도록 할 수도 있다:

$hash{ shift() }
$hash{ +shift }
$hash{ shift @_ }

-w 스위치를 사용시 만약 예약어를 문자열로 해석하면 경고 메시지를 사용자에게 준다. 왜냐 하면 사용자가 예약어를 일반 문자로 사용하고자 할 수 있기 때문이다.(그래서 ${package}나 ${pack}대신에 ${pkg}를 사용하기를 권하는 바이다. 왜냐 하면 그렇게 함으로써 경고 메시지를 피할 수 있기 때문이다.)

해쉬 키로 동작하지 않는 하드 레퍼런스

앞서 말한 것과 일관성을 유지하기 위하여 해쉬 키는 내부적으로 문자열[4]로 저장된다. 만약 사용자가 해쉬에 하드 레퍼런스를 키로 저장하고자 하면, 그 키 값은 문자열로 변환된다:

$x{ \$a } = $a;
($key, $value) = each %x;
print $$key; # 틀렸음

앞에서 이미 문자열을 하드 레퍼런스로 변환이 불가능하다는 점을 얘기했다. 따라서, 만약 단지 문자열을 내포함 변수 $key를 참조하려 한다면 그것은 하드 레퍼런스를 하지 않고, 오히려 심볼릭 레퍼런스를 한다. 그리고, 사용자가 SCALAR(0x1fc0e) 명의 변수를 가지지 않기 때문에 사용자는 시도하고자 하는 바를 얻을 수 없다. 사용자는 다음과 같이 하고자 할 수 있다:

$r = \@a;
$x{ $r } = $r;

그리고 적어도 사용자는 하드 레퍼런스가 아닌 키 대신에, 하드 레퍼런스인 해쉬 값을 사용할 수 있다. 비록 사용자가 하드 레퍼런스를 키로 저장할 수는 없지만, 만약 사용자가 문자열로 된 문의 하드 레퍼런스를 사용하면 레퍼런스의 주소가 결과인 문자열의 한 부분으로 포함되기 때문에 유니크(unique) 문자열을 생성하는 것이 보장된다.

그 결과 사용자는 사실 하드 레퍼런스를 다양한 해쉬 키로 사용할 수 있다. 나중에 사용자는 그것을 참조 할 수 없다.

리스트의 리스트

많은 종류의 중첩된 데이터 구조가 있다. 데이터 구조를 구축하는 가장 쉬운 방법은 리스트의 리스트이다.(또한 배열의 배열 혹은 다차원 배열이라 하기도 한다) 그것은 이해하기 쉬우며 여기서 적용하는 대부분의 것은 더 세련된 데이터 구조에도 적용이 가능하다.

구성과 접근

다음은 2차원 배열 값을 어떻게 설정하는 가를 보여준다:

# 리스트의 리스트를 배열에 할당

@LoL = (
    [ “fred”, “barney” ],
    [ “george”, “jane”, “elroy” ],
    [ “homer”, “marge”, “bart” ],
);
print $LoL[2][2]; # “bart”를 출력

전체 리스트는 대괄호가 아니라 괄호에 의해 싸인다. 그것은 사용자가 배열에 리스트를 할당하기 때문이다. 만약 사용자가 그 결과로 리스트가 아니라, 배열의 레퍼런스를 원하면 사용자는 바깥쪽에 대괄호를 사용한다:

# 스칼라 변수에 리스트 레퍼런스의 리스트에 대한 레퍼런스 할당

$ref_to_LoL = [
    [ “fred”, “barney”, “pebbles”, “bambam”, “dino”, ],
    [ “homer”, “bart”, “marge”, “maggie”, ],
    [ “george”, “jane”, “elroy”, “judy”, ],
];
print $ref_to_LoL->[2][2]; # “elroy”를 출력

$ref_to_LoL는 배열의 레퍼런스이며, @LoL는 (리스트를 나타내는)괄호는 (배열의 레퍼런스 생성을 나타내는) 대괄호로 바뀌었다. C 언어와는 달리 Perl은 사용자가 자유롭게 배열과 배열의 레퍼런스를 상호 교환하는 것을 허용치 않는다. 이것은 하나의 특징이다. 인접한 각 쌍의 중괄호나 대괄호 사이에는 암묵적인 ->가 존재한다. 따라서, 다음 두 행은

$LoL[2][2]
$ref_to_LoL->[2][2]

다음과 동일하다.

$LoL[2]->[2]
$ref_to_LoL->[2]->[2]

그러나, 첫번째 쌍의 대괄호 앞에 암묵적인 ->가 존재하지 않는다. 이는 $ref_to_LoL의 디레퍼런스가 -> 를 필요로 하는 이유가 된다.

자신의 것을 성장시킴

큰 리스트 할당은 고정된 데이터 구조를 생성하는데 매우 적합하다. 그러나, 만약 사용자가 작업이 진행되는 각 요소를 계산하고자 하면 어떻게 될까? 혹은 그렇지 않고 구조를 조금씩 만들면 어떻게 될까? 먼저 파일에서 데이터 구조를 읽어 들이는 것에 대해 살펴본다. 여기서 우리는 평면적인 파일을 가정하며, 그 파일의 각 행은 구조의 행으로 구성되며, 각 단어는 요소로 구성되어 있다고 가정한다. 다음은 어떻게 진행되는지를 보여준다:

while (<>) {
    @tmp = split;
    push @LoL, [ @tmp ];
}

사용자는 함수로부터 배열을 읽어들일 수도 있다:

for $i ( 1 .. 10 ) {
    @tmp = somefunc($i);
    $LoL[$i] = [ @tmp ];
}

물론, 사용자는 임시 변수를 사용할 필요는 없다:

while (<>) {
    push @LoL, [ split ];
}

그리고:

for $i ( 1 .. 10 ) {
    $LoL[$i] = [ somefunc($i) ];
}

또 사용자는 push 문을 사용할 필요가 없다. 사용자는 현재 배열의 어느 부분을 처리하고 있는지를 추적할 수 있다. 그리고, 배열의 적절한 행에 파일의 각 줄을 할당할 수 있다.

my (@LoL, $i, $line);
for $i ( 0 .. 10 ) { # just first 11 lines
    $line = <>;
    $LoL[$i] = [ split ‘ ‘, $line ];
}

단순화하여 매개 변수에 라인(line)을 할당하는 것을 피할 수 있다:

my (@LoL, $i);
    for $i ( 0 .. 10 ) { # just first 11 lines
    $LoL[$i] = [ split ‘ ‘, <> ];
}

대개 사용자는 그런 것에 대하여 명시적으로 언급하지 않은 채 스칼라 문에서 <>와 같은 잠재적 리스트 함수를 사용하는 것에 대해 주의해야 한다. 다음 예제는 일반적으로 가독성이 좋은 예이다:

my (@LoL, $i);
    for $i ( 0 .. 10 ) { # just first 11 lines
    $LoL[$i] = [ split ‘ ‘, scalar(<>) ];
}

만약 사용자가 $ref_to_LoL 변수가 배열의 레퍼런스이기를 원하면 다음과 같이 한다:

my $ref_to_LoL;
while (<>) {
    push @$ref_to_LoL, [ split ];
}

리스트의 리스트에 새로운 행을 추가하는 것은 좋다고 치고 넘어간다. 새로운 열을 추가하는 것은 어떠한가? 만약 사용자가 행렬을 처리하고자 하면, 단순한 할당 방법을 사용하는 것이 종종 가장 쉬운 방법이 된다.

for $x (1 .. 10) {
    for $y (1 .. 10) {
        $LoL[$x][$y] = func($x, $y);
    }
}
for $x ( 3, 7, 9 ) {
    $LoL[$x][20] += func2($x);
}

@LoL의 각 요소들이 이미 거기에 존재하는지의 여부는 문제가 되지 않는다. Perl은 기꺼이 사용자를 위해 생성하며, 필요하면 방해되는 요소는 정의되지 않은 값으로 설정한다. 만약 사용자가 열에 무엇인가를 추가하고자 하면 약간 재미있는 작업을 해야 한다.:

# 기존의 행에다 새로운 열을 추가

push @{ $LoL[0] }, “wilma”, “betty”;

앞의 것은 동작하지 않음에 주의하라.

push $LoL[0], “wilma”, “betty”; # 틀렸음!

사실, 위의 것은 컴파일조차 되지 않는다. 왜냐하면 push 문의 인자는 배열의 레퍼런스가 아니라 배열 그 자체여야 한다. 따라서, 첫번째 인자는 절대적으로 문자 @로 시작해야 한다. @뒤에 나오는 문자들은 어느 정도 고려해 볼 수 있다.

접근과 출력

이제부터 사용자 데이터 구조를 출력하는 방법에 대해 설명하고자 한다. 만약 사용자가 하나의 요소를 출력하고자 하면 다음과 같이 한다:

print $LoL[0][0];

만약 전체를 출력하고자 하면, 다음과 같이 단순히 해서는 안 된다:

print @LoL; # 틀렸음

여기서 레퍼런스는 리스트되고, Perl은 결코 사용자를 위해 자동적으로 씽이를 디레퍼런스 하지는 않는다. 대신에 사용자는 스스로 한 번이나 두 번 루프 작업을 해야 한다. 다음 코드는 한쌍의 외장형 서브스크립트를 루프 형태로 구성자에 대한 쉘-스타일을 이용하여 전체 구조를 출력한다.:

for $array_ref ( @LoL ) {
    print “\t [ @$array_ref ],\n”;
}

대괄호에 대해 잘 알아야 한다. 앞의 예와 다음 예에서 대괄호(서브스크립팅이 아닌)는 레퍼런스 생성을 나타내지는 않는다. 대괄호는 인용 부호내의 문자열안에 존재하지, 항목이 필요한 부분에 존재하지 않는다. 따라서, 그 자신의 특수한 의미를 상실한다. 그것은 출력하는 문자열의 한 부분에 불과하다. 만약 사용자가 서브스크립트(subscripts)를 추적하고자 하면 다음과 같이 한다:

for $i ( 0 .. $#LoL ) {
    print “\t element $i is [ @{$LoL[$i]} ],\n”;
}

혹은 다음과 같이 할 수도 있다(내부 루프를 눈여겨 보라):

for $i ( 0 .. $#LoL ) {
    for $j ( 0 .. $#{$LoL[$i]} ) {
        print “element $i $j is $LoL[$i][$j]\n”;
    }
}

사용자가 보듯이 여러모로 좀 더 복잡해진다. 따라서, 사용자 방식대로 임시 변수를 사용하는 것이 때때로는 더 쉬운 방법이다:

for $i ( 0 .. $#LoL ) {
    $aref = $LoL[$i];
    for $j ( 0 .. $#{$aref} ) {
        print “element $i $j is $aref->[$j]\n”;
    }
}

그러나, 아직까지 세련되지는 못하다. 따라서 다음과 같이 한다:

for $i ( 0 .. $#LoL ) {
    $aref = $LoL[$i];
    $n = @$aref – 1;
    for $j ( 0 .. $n ) {
        print “element $i $j is $aref->[$j]\n”;
    }
}

슬라이스(Slice)

만약 다차원 배열에서 슬라이스(slice: 열의 한 부분)에 대해 처리하고자 하면 사용자는 약간의 세련된 서브스크립팅을 해야한다. 왜냐하면 우리가 포인터 화살표를 통한 단일 요소에 대해 그럴듯한 비슷한 것을 가지고 있어도 슬라이스에 대해서는 그런 편리한 것이 존재하지 않는다. 그러나, 사용자는 슬라이스 연산을 하기위해 항상 루프를 작성할 수 있다. 다음은 루프를 사용하여 2차원 배열의 일부인 서브배열의 1차원 슬라이스를 생성하는 방법이다. 여기서 리스트의 리스트 변수를 가정한다(리스트의 리스트에 대한 레퍼런스보다는):

@part = ();
$x = 4;
for ($y = 7; $y < 13; $y++) {
    push @part, $LoL[$x][$y];
}

같은 루프는 슬라이스 연산으로 대치가 가능하다:

@part = @{ $LoL[4] } [ 7..12 ];

만약 사용자가 2차원 슬라이스(slice)를 원하면, 가령 $x는 4에서 8까지, $y는 7에서 12까지의 값을 가질 때, 다음은 그 한 방법의 예이다:

@newLoL = ();
for ($startx = $x = 4; $x <= 8; $x++) {
    for ($starty = $y = 7; $y <= 12; $y++) {
        $newLoL[$x – $startx][$y – $starty] = $LoL[$x][$y];
    }
}

앞의 예제에서는 @newLoL의 각 배열내의 값들에 대해 @LoL의 적절한 위치에 존재하는 값들로 하나씩 할당된다. 다른 방법은 익명의 배열을 생성하는 것이다. 이 익명의 배열은 @LoL의 서브배열이 원하는 슬라이스(slice)로 구성이 되며 이들 익명의 배열에 대한 레퍼런스는 @newLoL에 저장된다. 따라서, 우리는 이중으로 서브스크립트 처리된 @newLol에 대한 서브배열 값 대신에 @newLoL(말하자면 일단 서브스크립트 처리된)에다 레퍼런스를 작성한다. 이 방법은 가장 안쪽의 루프를 사용하지 않도록 해 준다:

for ($x = 4; $x <= 8; $x++) {
    push @newLoL, [ @{ $LoL[$x] } [ 7..12 ] ];
}

물론, 만약 사용자가 이것을 자주 사용하면 사용자는 extract_rectangle()와 같은 서브루틴을 아마 작성하는 것이 필요할 것이다.

일반적인 실수

앞에서 언급했듯이 Perl에서 배열이나 해쉬는 1차원으로 구현된다. “다차원” 배열 역시 1차원이다. 그러나, 1차원 배열에서의 그 값들은 다른 데이터 구조의 레퍼런스이다. 만약 사용자가 그들에 대한 참조없이 이들 값을 출력하고자 하면 레퍼런스된 데이터 보다 그 레퍼런스를 얻을 수 있다. 예를 들면, 다음 2행은:

@LoL = ( [2, 3], [4, 5, 7], [0] );

print “@LoL”;

다음 결과를 놓는다:

ARRAY(0x83c38) ARRAY(0x8b194) ARRAY(0x8b1d0)

한편, 다음 행은:

print $LoL[1][2];

결과로 7을 생성한다.

Perl은 사용자가 참조 매카니즘을 사용할 때만 사용자의 변수를 참조 할 수 있다. 그러나, $LoL[1][2]은 $LoL[1]->[2]의 간편한 표기 방법이며, $LoL[1]->[2]는 ${$LoL[1]}[2]의 간편한 표기 방법임을 기억하라. 사실 사용자는 중괄호를 사용하여 디레퍼런싱 연산을 작성할 수 있다. 그러나, 그것은 훨씬 보기 좋지 못하다. 사용자의 프로그램을 좀 더 좋게 해주기 위해 Perl이 제공하는 문법적인 요소를 사용하라. @LoL는 레퍼런스를 값으로 가지는 배열로 정의되었었다. 다음은 비슷하게 보이나 다른 경우의 예이다:

my $listref = [
    [ “fred”, “barney”, “pebbles”, “bambam”, “dino”, ],
    [ “homer”, “bart”, “marge”, “maggie”, ],
    [ “george”, “jane”, “elroy”, “judy”, ],
];
print $listref[2][2]; # 틀렸음!

여기서 $listref는 배열이 아니며, 배열을 참고하는 스칼라 변수이다. 이 경우 익명의 다차원 배열을 참고하며, 그 배열은 바깥쪽 대괄호에 의해 생성된다. 따라서, 이 예제에서 elroy를 출력하고자 하면 다음과 같이 해야 한다:

print $listref->[2][2];

대조적으로 잘못된 출력문의 $listref[2]는 아직까지 선언되지 않은 배열의 두 번째 요소이다. 만약 사용자가 다음을 요구하면

use strict ‘vars’; # or just use strict

즉 선언되지 않은 배열을 사용하면 컴파일시 오류가 발생한 것으로 메시지를 받을 수 있다. 배열의 배열을 구축할 경우 도터(daughter) 배열의 레퍼런스를 챙기는 것을 잊지 말라. 그렇지 않으면 사용자는 다음과 같이 단지 도터(daughter) 배열의 요소의 개수를 내포한 배열을 생성하게 된다:

for $i (1..10) {
    @list = somefunc($i);
    $LoL[$i] = @list; # 틀림!
}

여기서 우리는 @list에 대해 스칼라 문으로 접근한다. 따라서, 그 결과인 배열의 요소 개수가 $LoL[$i].에 할당된다. 레퍼런스를 처리하는 적절한 방법에 대해 곧 이어 소개한다. 흔히 범하는 오류는 같은 메모리 위치에 반복하여 레퍼런스를 취하는 것이다:

for $i (1..10) {
    @list = somefunc($i);
    $LoL[$i] = \@list; # WRONG!
}

for 루프의 두번째 줄에서 생성되는 모든 레퍼런스는 같다. 다시 말해 단일 배열 @list의 레퍼런스인 것이다. 그렇다, 이 배열은 루프를 도는 동안 매번 다른 값을 저장하게 되는 것이다. 그러나, 모든 것이 끝난 후, $LoL는 같은 배열에 대한 동일한 레퍼런스를 가진다. 그 배열은 자신에게 할당된 가장 마지막 값을 가지고 있는 것이다. 다음은 좀 더 나은 방법이다:

for $i (1..10) {
    @list = somefunc($i);
    $LoL[$i] = [ @list ];
}

대괄호는 할당시에 @list에 저장된 값의 복사본을 이용하여 새로운 배열에 대한 레퍼런스를 생성한다. 비슷한 결과-비록 가독성이 떨어지지만-를 다음 예에 의해서 얻을 수 있다:

for $i (1..10) {
    @list = somefunc($i);
    @{$LoL[$i]} = @list;
}

$LoL[$i]는 레퍼런스이어야 하므로 자연스럽게 레퍼런스가 존재하게 된다. 그러면, 그 앞의 @가 이 새로운 레퍼런스를 참조한다. 그 결과 @list의 값은 $LoL[$i]에 레퍼런스 된 배열에 할당된다. 좀 더 명확하게 한다면 사용자는 이 구조를 피하기를 기도해야 할 것이다. 그러나, 사용자가 그것을 사용해야 할 경우도 있다. 가령 $LoL이 이미 배열의 레퍼런스로 구성된 배열이라고 가정해 보자, 다시 말해 사용자가 다음과 같은 할당을 한다고 가정하자:

$LoL[3] = \@original_list;

그리고, 사용자가 @original_list를 변경하기를 원한다고 가정하자(즉 $LoL의 4번째 열을 변경하기를 원한다고 하자), 그러면 그것은 @list의 요소를 참고한다. 이 코드는 동작한다:

@{$LoL[3]} = @list;

이 경우 레퍼런스 그 자신은 변하지 않는다, 그러나, 배열의 각 요소는 참고된다. 그러나, 사용자는 이 방식이 @original_list의 값을 덮어쓴다는 것을 인지할 필요가 있다. 다음의 위험해 보이는 코드는 사실 잘 동작한다:

for $i (1..10) {
    my @list = somefunc($i);
    $LoL[$i] = \@list;
}

그것은 my 변수가 루프를 돌면서 매번 새롭게 생성되기 때문이다. 그래서, 사용자가 같은 변수 레퍼런스를 매번 저장하는 것처럼 보일지라도 사용자는 실제로는 그렇지가 않은 것이다. 이것은 미묘한 차이점이나 그 기술을 통해 좀 덜 숙련된 프로그래머로 하여금 실수를 하게끔 할 수 있을 수도 있지만, 좀더 효율적인 코드를 생성할 수 있다. 그것은 최종 할당문에서는 복사하는 일이 없으므로 좀 더 효율적이다. 다른 한편으로는 만약 사용자가 값들을 복사해야만 한다면(앞의 첫번째 할당문이 하는 것), 사용자는 대괄호가 의미하는 복사값을 사용하고 가급적이면 임시 변수 사용을 피하는 것이 좋다:

for $i (1..10) {
    $LoL[$i] = [ somefunc($i) ];
}

요약하면 다음과 같다:

$LoL[$i] = [ @list ]; # 가장 안전하고 때로는 가장 빠르다
$LoL[$i] = \@list; # 빠르나 위험하며 리스트의 필요성에 따라 달라진다
@{ $LoL[$i] } = @list; # 대부분 사용하기에는 너무 힘들다

데이터 구조 코드 예

일단 사용자가 다차원 배열(리스트의 리스트)의 생성 및 이용 방법에 대하여 습득하였으면, 사용자는 좀 더 복잡한 데이터 구조를 생성하기를 원할 것이다. 만약 C 언어의 구조나 파스칼 레코드의 구조를 찾고자 하면, 사용자는 Perl이 사용자를 위해 그 일을 하기 위한 특별한 예약어를 제공하지 않는다는 것을 알게 될 것이다. 대신 사용자가 사용할 수 있는 것은 좀 더 유연성이 좋은 시스템인 것이다.

Perl은 데이터를 조작하는데 2가지 방법을 제공한다. 즉 배열에 저장된 정렬된 리스트와 위치에 의한 그에 대한 접근(access)이나 해쉬에 저장된 정렬되지 않은 키/값의 쌍과 이름을 통한 접근(access) 2가지가 존재한다. Perl에서 레코드를 표현하는 가장 좋은 방법은 해쉬 레퍼런스를 이용하는 것이다. 그러나, 그런 레코드를 조직화하는 방법은 변화한다. 사용자는 이들 레코드의 정렬된 리스트를 계속 유지하면서 숫자를 통해 검색하기를 원할 수도 있다. 그 경우 사용자는 레코드(해쉬 레퍼런스)를 저장할 배열을 사용할 수 있다. 그러나, 사용자는 레코드를 이름을 통해 검색하기를 원할 수도 있는데, 그럴 경우 사용자는 다른 해쉬에다 그것을 저장할 수 있다. 또한 사용자는 동시에 그 두 가지를 다 할 수 있다: 배열과 해쉬는 같은 레코드에 대한 레퍼런스를 각각 가질 수 있으며, 그 레코드는 익명의 해쉬 씽이이며, 그 각각은 사용자가 어느 정도의 합리성안에서 원하는 만큼 레퍼런스를 가질 수 있다.

다음 부분에서는 사용자는 5개의 데이터 구조의 각각에 대하여 어떻게 구성하고, 생성하고, 접근하는 지에 관해 상세한 예제를 설명한다. 앞의 4가지 예는 직설적인 동종의 배열과 해쉬의 조합에 대한 것이면, 나머지 1개는 덜 규칙적인 데이터 구조의 사용방법을 보여주는 것이다. 이들 예는 설명이 좀 부족하지만, 사용자가 이미 이장의 앞에서 설명된 내용들에 대해 충분히 알고 있다고 가정하고 설명된다.

배열의 배열

사용자가 기본적인 2차원 행렬을 원하면 배열의 배열을 사용하라. 하나의 응용 예를 들자면 사용자 망에 연결된 모든 호스트의 리스트를 제작하는 것이다. 그런데, 이들 호스트 각각은 여러 개의 앨리어스(aliases)를 가지고 있을 수 있다. 다른 응용 예를 들자면 일상 메뉴 리스트가 있다. 그 메뉴의 각각은 그날 제공되는 음식 리스트가 되는 것이다. 다른 예를 들자면, 유명한 TV 인물들의 리스트를 작성하는 것이다. 이때 그 인물들은 하나의 큰 리스트의 리스트에 저장되어 있다.

배열의 배열 구성

@LoL = (
    [ “fred”, “barney” ],
    [ “george”, “jane”, “elroy” ],
    [ “homer”, “marge”, “bart” ],
);

배열의 배열 생성

# 파일로부터 읽기

while ( <> ) {
    push @LoL, [ split ];
}

# 함수 호출

for $i ( 1 .. 10 ) {
    $LoL[$i] = [ somefunc($i) ];
}

# 임시 변수 사용

for $i ( 1 .. 10 ) {
    @tmp = somefunc($i);
    $LoL[$i] = [ @tmp ];
}

# 기존의 행에다 추가

push @{ $LoL[0] }, “wilma”, “betty”;

배열의 배열에 대한 접근 및 출력

# 1개의 요소

$LoL[0][0] = “Fred”;

# 다른 요소

$LoL[1][1] =~ s/(\w)/\u$1/;

# ref를 이용한 전체 출력

for $array_ref ( @LoL ) {
    print “\t [ @$array_ref ],\n”;
}

# 인덱스를 이용한 전체 출력

for $i ( 0 .. $#LoL ) {
    print “\t [ @{$LoL[$i]} ],\n”;
}

# 전체 출력

for $i ( 0 .. $#LoL ) {
    for $j ( 0 .. $#{$LoL[$i]} ) {
        print “element $i $j is $LoL[$i][$j]\n”;
    }
}

배열의 해쉬

사용자가 단순히 지수(index number)보다 특정한 문자열에 의해 각 배열을 검색하고자 할 때 배열의 해쉬를 사용하라. TV 인물의 예를 들면, 0번째, 1번째, 등등에 의한 이름 리스트를 검색하기 보다는 그 쇼의 이름에 따라 배역 리스트를 검색할 수 있도록 만드는 것이다. 외장 데이터 구조가 해쉬 구조이기 때문에 내용물의 순서에 대한 정보는 다 잃어버린다. 그것은 사용자가 출력하고자 할 때 그 순서를 예측할 수 없다는 것이다. 사용자는 sort 함수를 호출하여 사용자가 원하는 순서형태로 결과를 출력할 수 있다.

배열의 해쉬 구성

# 우리는 키가 식별자인 경우에는 인용 부호를 관례적으로 사용하지 않는다

%HoL = (
    flintstones => [ “fred”, “barney” ],
    jetsons => [ “george”, “jane”, “elroy” ],
    simpsons => [ “homer”, “marge”, “bart” ],
);

배열로 구성된 해쉬의 생성

# 다음의 포맷으로 파일을 읽는다.

# flintstones: fred barney wilma dino
while ( <> ) {
    next unless s/^(.*?):\s*//;
    $HoL{$1} = [ split ];
}
# 파일 일기; 더많은 임시 변수
# flintstones: fred barney wilma dino
while ( $line = <> ) {
    ($who, $rest) = split /:\s*/, $line, 2;
    @fields = split ‘ ‘, $rest;
    $HoL{$who} = [ @fields ];
}
# 배열을 반환값으로 주는 함수를 호출
for $group ( “simpsons”, “jetsons”, “flintstones” ) {
    $HoL{$group} = [ get_family($group) ];
}
# 비슷하나, 임시 변수를 사용
for $group ( “simpsons”, “jetsons”, “flintstones” ) {
    @members = get_family($group);
    $HoL{$group} = [ @members ];
}
# 기존 가족에다 새로운 구성원을 추가
push @{ $HoL{flintstones} }, “wilma”, “betty”;

배열로 구성된 해쉬의 접근 및 출력

# 1개의 요소
$HoL{flintstones}[0] = “Fred”;
# 다른 요소
$HoL{simpsons}[1] =~ s/(\w)/\u$1/;
# 전체를 출력
foreach $family ( keys %HoL ) {
    print “$family: @{ $HoL{$family} }\n”;
}
# 인덱스를 이용하여 전체를 출력
foreach $family ( keys %HoL ) {
    print “$family: “;
    foreach $i ( 0 .. $#{ $HoL{$family} } ) {
        print ” $i = $HoL{$family}[$i]”;
    }
}
print “\n”;
# 구성원의 숫자로 정렬된 전체를 출력
foreach $family ( sort { @{$HoL{$b}} <=> @{$HoL{$a}} } keys %HoL ) {
    print “$family: @{ $HoL{$family} }\n”
}
# 구성원의 숫자와 이름으로 정렬된 전체를 출력
foreach $family ( sort { @{$HoL{$b}} <=> @{$HoL{$a}} } keys %HoL ) {
    print “$family: “, join(“, “, sort @{ $HoL{$family} }), “\n”;
}

해쉬로 구성된 배열

가령 사용자가 대량의 키/값의 쌍으로 구성된 레코드를 보유하고 있고, 그 레코드에 대해 순차적으로 접근하고자 하는 경우 해쉬로 구성된 배열을 사용할 수 있다. 이 배열은 다른 동종의 데이터 구조보다는 덜 자주 사용된다.

해쉬로 구성된 배열의 생성

@LoH = (

{

lead => “fred”,

friend => “barney”,

},

{

lead => “george”,

wife => “jane”,

son => “elroy”,

},

{

lead => “homer”,

wife => “marge”,

son => “bart”,

},

);

해쉬로 구성된 배열의 생성

# 파일로부터 읽기

# 변수: lead=fred friend=barney

while ( <> ) {

$rec = {};

for $field ( split ) {

($key, $value) = split /=/, $field;

$rec->{$key} = $value;

}

push @LoH, $rec;

}

# 파일로부터 읽기

# 포맷: lead=fred friend=barney

# 임시 변수 사용하지 않음

while ( <> ) {

push @LoH, { split /[\s=]+/ };

}

# “lead”,”fred”,”daughter”,”pebbles”와 같이 키와 값 배열을 반환하는 함수 호출

while ( %fields = getnextpairset() ) {

push @LoH, { %fields };

}

# 비슷하나 임시 변수는 사용하지 않음

while (<>) {

push @LoH, { parsepairs($_) };

}

# 요소에 키와 값을 더함

$LoH[0]{pet} = “dino”;

$LoH[2]{pet} = “santa’s little helper”;

해쉬로 구성된 배열의 접근 및 출력

# 1개의 요소

$LoH[0]{lead} = “fred”;

# 다른 요소

$LoH[1]{lead} =~ s/(\w)/\u$1/;

# ref를 이용하여 전체 출력

for $href ( @LoH ) {

print “{ “;

for $role ( keys %$href ) {

print “$role=$href->{$role} “;

}

print “}\n”;

}

# 인덱스를 이용하여 전체 출력

for $i ( 0 .. $#LoH ) {

print “$i is { “;

for $role ( keys %{ $LoH[$i] } ) {

print “$role=$LoH[$i]{$role} “;

}

print “}\n”;

}

# 한번에 전체를 출력

for $i ( 0 .. $#LoH ) {

for $role ( keys %{ $LoH[$i] } ) {

print “element $i $role is $LoH[$i]{$role}\n”;

}

}

해쉬로 구성된 해쉬

다차원 해쉬는 Perl의 동종 구조중 가장 유연성이 뛰어나다. 그것은 다른 레코드를 내포하는 레코드를 구성하는 것과 같다. 각 단계에서 사용자는 문자열(만약 스페이스를 내포하면 인용부호 사용)을 이용하여 해쉬에 인덱스를 매긴다. 그러나, 해쉬에 저장된 한 쌍의 키/값은 특정한 순서로 나오지 않는다. 만약 순서가 중요하다면 사용자는 스스로 정렬 코드를 구성해야 한다.

해쉬로 구성된 해쉬의 생성

%HoH = (

flintstones => {

lead => “fred”,

pal => “barney”,

},

jetsons => {

lead => “george”,

wife => “jane”,

“his boy” => “elroy”, # key quotes needed

},

simpsons => {

lead => “homer”,

wife => “marge”,

kid => “bart”,

},

);

해쉬로 구성된 해쉬의 생성

# 파일로부터 읽기

# flintstones: lead=fred pal=barney wife=wilma pet=dino

while ( <> ) {

next unless s/^(.*?):\s*//;

$who = $1;

for $field ( split ) {

($key, $value) = split /=/, $field;

$HoH{$who}{$key} = $value;

}

}

# 파일로부터 읽기; 임시 변수들

while ( <> ) {

next unless s/^(.*?):\s*//;

$who = $1;

$rec = {};

$HoH{$who} = $rec;

for $field ( split ) {

($key, $value) = split /=/, $field;

$rec->{$key} = $value;

}

}

# 내부 해쉬에 대한 키,값을 반환하는 함수 호출

for $group ( “simpsons”, “jetsons”, “flintstones” ) {

$HoH{$group} = { get_family($group) };

}

# 비슷하나, 임시 변수를 사용

for $group ( “simpsons”, “jetsons”, “flintstones” ) {

%members = get_family($group);

$HoH{$group} = { %members };

}

# 생성된 내부 해쉬의 레퍼런스를 포함하여 외부 해쉬를 반환 값으로 주는 함수 호출

sub hash_families {

my @ret;

foreach $group ( @_ ) {

push @ret, $group, { get_family($group) };

}

@ret;

}

%HoH = hash_families( “simpsons”, “jetsons”, “flintstones” );

# 기존 가족에다 새로운 멤버를 추가

%new_folks = (

wife => “wilma”,

pet => “dino”;

);

for $what (keys %new_folks) {

$HoH{flintstones}{$what} = $new_folks{$what};

}

해쉬로 구성된 해쉬 액세스 및 출력

# 한 개의 요소

$HoH{flintstones}{wife} = “wilma”;

# 다른 요소

$HoH{jetsons}{‘his boy’} =~ s/(\w)/\u$1/;

# 전체 데이터 출력

foreach $family ( keys %HoH ) {

print “$family: “;

foreach $role ( keys %{ $HoH{$family} } ) {

print “$role=$HoH{$family}{$role} “;

}

print “\n”;

}

# 임시 변수를 이용한 전체 데이터 출력

while ( ($family,$roles) = each %HoH ) {

print “$family: “;

while ( ($role,$person) = each %$roles ) { # using each precludes sorting

print “$role=$person “;

}

print “\n”;

}

# 일부 정렬된 전체 데이터 출력

foreach $family ( sort keys %HoH ) {

print “$family: “;

foreach $role ( sort keys %{ $HoH{$family} } ) {

print “$role=$HoH{$family}{$role} “;

}

print “\n”;

}

# 멤버 수로 정렬된 전체 데이터 출력

foreach $family ( sort { keys %{$HoH{$a}} <=> keys %{$HoH{$b}} } keys %HoH ) {

print “$family: “;

foreach $role ( sort keys %{ $HoH{$family} } ) {

print “$role=$HoH{$family}{$role} “;

}

print “\n”;

}

# 각 역할에 대한 정렬 순서(계급 순서)를 정립

$i = 0;

for ( qw(lead wife son daughter pal pet) ) { $rank{$_} = ++$i }

# 이제 멤버의 수에 따라 정렬된 전체를 출력

foreach $family ( sort { keys %{$HoH{$a}} <=> keys %{$HoH{$b}} } keys %HoH ) {

print “$family: “;

# 계급 순서에 따라 출력

foreach $role ( sort { $rank{$a} <=> $rank{$b} } keys %{ $HoH{$family} } ) {

print “$role=$HoH{$family}{$role} “;

}

print “\n”;

}

좀 더 정교한 레코드

그것들은 단순한 2단계 형태의 동종 데이터 구조였다.: 각 요소는 모든 다른 요소가 하는 것처럼 같은 종류의 씽이를 포함한다. 그것은 반드시 그런 식으로 될 필요는 없다. 모든 요소는 모든 종류의 스칼라를 가질 수 있다. 이는 그것이 문자열, 숫자, 혹은 어떤 것의 레퍼런스(배열 혹은 해쉬의 레퍼런스 이외의 더 색다른 것도 포함하며, 이때 레퍼런스는 이름을 가지 혹은 익명의 함수 또는 오파크(opaque) 객체를 의미한다) 등을 의미한다. 사용자가 할수 없는 일은 주어진 스칼라에 동시에 2 종류 이상의 씽이를 저장할 수 없다는 것이다. 만약 사용자가 그 일을 하고자 하면, 그것은 사용자가 다음 하위 단계에서 사용자가 덮어 씌우려고 하는 다른 종류의 씽이를 처리하기 위해, 배열 혹은 해쉬를 만들어야 한다는 것을 나타내는 신호이다. 아래에서 사용자는 레코드에 저장하고자 하는 모든 가능한 종류의 것을 설명하는 예제를 볼 수 있다. 기본 구조로 우리는 해쉬 레퍼런스를 사용한다. 키는 대문자 문자열이며, 해쉬가 일반적인 조합 배열이 아니라 특정 레코드 형태로 사용될 때에는 가끔 관례를 사용한다.

더 세련된 레코드 작성

다음은 다양한 종류의 필드로 구성된 레코드의 생성과 사용 방법을 보여 준다:

$rec = {

TEXT => $string,

SEQUENCE => [ @old_values ],

LOOKUP => { %some_table },

THATCODE => \&some_function,

THISCODE => sub { $_[0] ** $_[1] },

HANDLE => \*STDOUT,

};

print $rec->{TEXT};

print $rec->{SEQUENCE}[0];

$last = pop @{ $rec->{SEQUENCE} };

print $rec->{LOOKUP}{“key”};

($first_k, $first_v) = each %{ $rec->{LOOKUP} };

# 이름을 가진 혹은 안가진 subs를 호출하는 것에 차이점은 없음

$answer = &{ $rec->{THATCODE} }($arg);

$answer = &{ $rec->{THISCODE} }($arg1, $arg2);

# 각접적인 객체 슬롯상의 별도의 중괄호를 가져야 한다

print { $rec->{HANDLE} } “a string\n”;

use FileHandle;

$rec->{HANDLE}->autoflush(1);

$rec->{HANDLE}->print(“a string\n”);

더 세련된 레코드 작성

%TV = (

flintstones => {

series => “flintstones”,

nights => [ qw(monday thursday friday) ],

members => [

{ name => “fred”, role => “lead”, age => 36, },

{ name => “wilma”, role => “wife”, age => 31, },

{ name => “pebbles”, role => “kid”, age => 4, },

],

},

jetsons => {

series => “jetsons”,

nights => [ qw(wednesday saturday) ],

members => [

{ name => “george”, role => “lead”, age => 41, },

{ name => “jane”, role => “wife”, age => 39, },

{ name => “elroy”, role => “kid”, age => 9, },

],

},

simpsons => {

series => “simpsons”,

nights => [ qw(monday) ],

members => [

{ name => “homer”, role => “lead”, age => 34, },

{ name => “marge”, role => “wife”, age => 37, },

{ name => “bart”, role => “kid”, age => 11, },

],

},

);

복잡한 레코드로 구성된 해쉬의 생성

Perl은 복잡한 데이터 구조를 파싱하는데 아주 유용하며, 사용자는 별도의 파일에 정규 Perl 코드로 데이터 선언을 한 후 dorequire 문을 이용하여 로딩하여 사용할 수 있다. 이들 함수에 대한 상세한 내용은 3장의 함수를 참고하라.

# 다음은 하나씩 하나씩 구축한다

$rec = {};

$rec->{series} = “flintstones”;

$rec->{nights} = [ find_days() ];

@members = ();

# 이 파일은 field=value 문법이라 가정한다

while (<>) {

%fields = split /[\s=]+/;

push @members, { %fields };

}

$rec->{members} = [ @members ];

# 이제 전체를 기억하라

$TV{ $rec->{series} } = $rec;

사용자는 데이터를 복사함에 따른 중복을 피하기 위해 별도의 포인터 필드를 사용할 수 있다. 예를 들면, 개인별 레코드에 “kids”라는 필드를 추가하고자 하는 경우, 이 것은 ‘kids’의 레코드에 대한 레퍼런스들로 구성된 리스트에 대한 레퍼런스일 수 있다. 그렇게 함으로써 사용자는 같은 데이터를 2개의 기억장소에 가지는 경우에 발생하는 갱신 문제를 피할 수 있다.

foreach $family (keys %TV) {

my $rec = $TV{$family}; # 임시 포인터

@kids = ();

for $person ( @{$rec->{members}} ) {

if ($person->{role} =~ /kid|son|daughter/) {

push @kids, $person;

}

}

# 유의: $rec 와 $TV{$family} 는 같은 데이터를 가르킨다!!

$rec->{kids} = [ @kids ];

}

# 사용자는 배열을 복사했다. 그러나, 그 배열 자체는 복자되지 않는

# 객체의 포인터를 내장한다.

# 이 말은 만약 사용자가 $TV{simpsons}{kids}[0]{age}++를 통해 바트(인물 이름)

# 를 늙게 만드는 경우 print $TV{simpsons}{members}[2]{age}의 결과 역시

# 변하게 된다는 것이다.

# 왜냐하면, $TV{simpsons}{kids}[0] 와 $TV{simpsons}{members}[2]는

# 같은 그 하부의 해쉬테이블을 가르키는 것이다.

# 전체를 출력한다

foreach $family ( keys %TV ) {

print “the $family”;

print ” is on during @{ $TV{$family}{nights} }\n”;

print “its members are:\n”;

for $who ( @{ $TV{$family}{members} } ) {

print ” $who->{name} ($who->{role}), age $who->{age}\n”;

}

print “it turns out that $TV{$family}{‘lead’} has “;

print scalar ( @{ $TV{$family}{kids} } ), ” kids named “;

print join (“, “, map { $_->{name} } @{ $TV{$family}{kids} } );

print “\n”;

}


Advertisements

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

%s에 연결하는 중