Perl의 @ARGV

@ARGV $ARGV[] $0 shift argc

여러분이 Perl 스크립트, 예를 들어 programming.pl을 작성하면, 그 프로그램의 사용자들은 명령행에서 perl programming.pl과 같이 하여 그 스크립트를 실행할 것입니다.

사용자들은 또한 perl programming.pl -a --machine remote /etc처럼 어떤 명령행 인자들을 전달할 수 있습니다. 사용자가 그러는 것을 막을 수는 없고, 스크립트는 이 값들을 무시할 것입니다. 그렇다면, 만일 전달된 값이 있다면 그게 어떤 값인지 스크립트의 작성자인 여러분이 어떻게 알 수 있을까요?

명령행(Command line)

Perl은 자동적으로 @ARGV라는 배열을 제공하며, 이 배열은 명령행에서 전달된 모든 값들이 담겨 있습니다. use strict를 사용하더라도 이 변수는 따로 선언하지 않아도 됩니다.

이 변수는 언제나 존재하며 명령행에서 전달된 값들이 자동으로 이 변수에 저장됩니다.

인자가 전혀 없다면, 이 배열은 비어 있을 것입니다. 명령행 인자가 단 하나 있다면, 이 값이 @ARGV 배열의 유일한 원소가 될 것입니다. 위의 예에서는 @ARGV에는 다음 원소가 들어있을 것입니다: -a, --machine, remote, /etc

실제 동작을 보도록 합시다:

이 코드를 programming.pl라는 이름으로 저장합니다:

use strict;
use warnings;
use Data::Dumper qw(Dumper);

print Dumper \@ARGV;

이것을 다음처럼 실행합니다: perl programming.pl -a --machine remote /etc 그러면 출력은 다음과 같습니다:

$VAR1 = [
          '-a',
          '--machine',
          'remote',
          '/etc'
        ];

보시다시피 @ARGV의 내용을 출력하기 위하여 Data::DumperDumper 함수를 사용하였습니다.

다른 프로그래밍 언어를 사용하다 오셨다면, 이게 궁금하실지 모릅니다: 이 Perl 프로그램의 이름은 어디 있지?

스크립트의 이름은 $0에 담긴다

실행되는 프로그램의 이름, 위의 경우 programming.pl은 언제나 $0 변수에 담깁니다. ($1, $2 등은 무관하니 주의하세요!)

C 프로그래머

여러분이 C 프로그래밍 언어를 아신다면, 이건 argv와 유사합니다만, Perl의 @ARGV에는 프로그램의 이름이 담기지 않는다는 차이가 있습니다. 이 이름은 $0 변수에 들어 있습니다. 또한 argc 같은 변수는 불필요합니다. 왜냐하면 scalar 함수를 쓰거나 배열을 스칼라 문맥에 넣음으로써 @ARGV 배열의 원소의 갯수를 쉽게 알아낼 수 있기 때문입니다.

유닉스/리눅스 쉘 프로그래밍

여러분이 유닉스/리눅스 쉘 프로그래밍의 세계에서 오셨다면 $0는 그 세계에서도 스크립트의 이름을 담고 있었다는 걸 아실 것입니다. 그러나 쉘에서는 $1, $2 등이 나머지 명령행 인자를 담고 있습니다. 이 변수들은 Perl에서는 정규 표현식에서 사용됩니다. 명령행 인자들은 @ARGV에 담기며, 유닉스/리눅스 쉘의 $*와 유사합니다.

@ARGV에서 명령행 인자들을 추출하는 방법

@ARGV는 평범한 Perl 배열입니다. 여러분이 생성하는 배열과의 유일한 차이는, 이 배열은 선언할 필요가 없고 스크립트가 시작될 때 Perl에 의해 내용이 채워진다는 것입니다.

추가로, 여러분은 이 배열을 평범한 배열로 다룰 수 있습니다. foreach를 사용하여 원소들을 순회하거나, 인덱스를 써서 하나씩 접근할 수 있습니다: $ARGV[0].

또한 이 배열에 shift, unshift, pop 또는 push를 사용할 수 있습니다.

사실, @ARGV의 내용을 가져올 수 있을 뿐 아니라, 내용을 수정할 수도 있습니다.

만일 명령행에 하나의 값이 있을 것으로 기대된다면 $ARGV[0]을 살펴봄으로써 그 값이 무엇인지, 또는 그 값이 제공되긴 했는지를 검사할 수 있습니다. 두 개의 값을 기대한다면 $ARGV[1]도 체크할 것입니다.

예를 들어서, 전화번호부를 생성해 봅시다. 이름을 넣으면, 그 이름에 해당하는 번호를 출력합니다. 이름과 번호를 넣으면 이 프로그램은 이 한 쌍을 "데이타베이스"에 저장합니다. (코드의 "데이타베이스" 부분을 실제로 다루지는 않고, 있는 것처럼 가정하겠습니다.)

우리는 매개변수가 $ARGV[0]에 들어오고 아마 $ARGV[1]에도 들어올 것을 알고 있습니다. 그러나 이것들은 배열의 첫번째와 두번째 원소라는 의미밖에 없습니다. 일반적으로는 $ARGV[0] 같은 것들 대신 여러분이 직접 이름지은 변수를 쓰는 게 낫습니다. 따라서 우리가 할 첫번째 일은 이 값들을 의미 있는 이름의 변수에 복사하는 것입니다:

다음과 같이 해도 동작하겠지만:

my $name   = $ARGV[0];
my $number = $ARGV[1];

이렇게 하는 게 훨씬 낫습니다:

my ($name, $number) = @ARGV;

이제 전체 예제(데이타베이스 부분을 제외하고)를 보도록 합시다. 다음 코드를 programming.pl로 저장합니다.

use strict;
use warnings;

my ($name, $number) = @ARGV;

if (not defined $name) {
  die "Need name\n";
}

if (defined $number) {
  print "Save '$name' and '$number'\n";
  # save name/number in database
  exit;
}

print "Fetch '$name'\n";
# look up the name in the database and print it out

@ARGV로부터 값들을 복사한 후에, 우리는 이름을 제공받았는지 검사합니다. 만일 아니라면, die를 호출해서 에러메시지를 출력하고 스크립트를 종료합니다.

이름이 주어졌다면, 그 다음은 번호가 있는지 검사합니다. 번호가 있다면 그것을 데이타베이스에 저장하고 (위에서는 구현되지는 않았습니다) 스크립트를 종료합니다.

번호가 없다면 데이타베이스에서 번호를 가져옵니다. (이것도 여기서는 구현되지 않았습니다.)

이게 어떻게 동작하는지 봅시다: ($ 기호는 프롬프트일 뿐이고 우리가 입력하는 게 아닙니다.)

$ perl programming.pl Foo 123
Save 'Foo' and '123'

$ perl programming.pl Bar 456
Save 'Bar' and '456'

$ perl programming.pl John Doe 789
Save 'John' and 'Doe'

처음 두 번은 괜찮았습니다만, 마지막은 좋아보이지 않습니다. 우리는 "John Doe"의 전화번호를 789로 저장하려고 했지만, 우리의 스크립트는 "John"의 전화번호가 "Doe"인 것처럼 저장했습니다.

이유는 간단하고, Perl과는 무관합니다. 어떤 다른 언어에서도 마찬가지일 것입니다. 여러분이 스크립트를 실행한 쉘이나 명령행은 그 라인을 분석하고 값들을 perl에 전달하여 perl이 그걸 @ARGV에 넣게 됩니다. 유닉스/리눅스 쉘과 윈도우의 명령행은 라인을 각 스페이스마다 분리합니다. 그래서 우리가 perl programming.pl John Doe 789라고 입력했을 때, 쉘은 실제로 3개의 매개변수를 스크립트에 전달하게 됩니다. 제대로 동작하게 하기 위해서 사용자는 공백을 포함한 값은 따옴표 안에 넣어줘야 합니다:

$ perl a.pl "John Doe" 789
Save 'John Doe' and '789'

여러분은 프로그래머로서 이 문제에 대해 그다지 할 수 있는 게 없습니다.

인자 검사하기

아마 여러분은 원소의 갯수가 여러분이 예상한 것보다 많지 않나 검사할 수 있을 것입니다. 이렇게 하면 사용자가 위와 같은 실수를 하는 것을 막을 수 있습니다만, 만일 사용자가 John Doe의 전화번호를 검색하기를 원하면서 따옴표를 쓰는 것을 잊는다면 어떻게 될까요:

perl a.pl John Doe
Save 'John' and 'Doe'

이 경우는 2개의 매개변수가 있고 이것은 정상적인 숫자입니다.

여기서도, 조금 개선하여 $number 변수의 내용이 전화번호로 받아들일 수 있는 형태인지를 검사할 수 있습니다. 이것은 이런 경우의 실수를 줄이게 될 것입니다. 이것은 여전히 완벽하지는 않고 만능의 해결책이 아닙니다: 다른 응용프로그램에서는 같은 제약이 걸린 매개변수가 여러 개 있을지도 모릅니다.

불행하게도 @ARGV를 "수작업으로" 해석할 때 우리가 할 수 있는 게 많지 않습니다. 다른 기사에서 삶을 조금 쉽게 만들어주는 Getopt::Long이나 유사한 라이브러리에 대해 적겠습니다만, 지금은 또다른 간단한 경우에 대해 알아봅시다.

한개의 매개변수 shift

흔한 경우 하나는 사용자가 파일 이름 하나를 명령행에 적을 것을 예상하는 경우입니다. 이 경우 다음과 같은 코드를 작성할 수 있습니다:

my $filename = shift or die "Usage: $0 FILENAME\n";

쉽게 설명하기 위하여 이 라인을 두 부분으로 나누어봅시다: my $filename = shift

보통 shift는 매개변수로 배열을 받습니다만, 이 경우는 매개변수 없이 사용하였습니다. 이런 경우 shift 는 기본적으로 @ARGV에 대하여 동작하게 됩니다. 따라서 위의 코드는 @ARGV 배열의 첫번째 값을 $filename 변수로 이동시킬 것입니다. (코드가 서브루틴 안에 있지 않는 한에는)

이러고 나면 기본적으로 다음과 같은 코드가 남습니다: $filename or die "Usage: $0 FILENAME\n"

이것은 불리언 식입니다. $filename에 파일의 이름이 들어 있다면 이것은 참으로 간주되고 스크립트는 or die ... 부분을 실행하지 않고 계속 진행할 것입니다. 반면에, 만일 @ARGV가 비어 있었다면, $filename에는 undef이 할당되고, 이것은 거짓으로 간주되어 Perl은 or 구문의 우변을 실행하게 되며, 메시지를 출력하고 스크립트를 종료합니다.

따라서 결과적으로, 이 코드는 명령행에 값이 전달되었는지를 검사합니다. 그 값은 $filename에 복사됩니다. 만일 전달된 값이 없다면, 스크립트는 죽게(die) 됩니다.

사소한 버그

위 코드에는 사소한 버그가 하나 있습니다. 만일 사용자가 파일의 이름으로 0을 입력할 경우, 이것은 역시 거짓으로 간주되고 스크립트는 이 파일을 다루는 것을 거부할 것입니다. 남은 질문은: 이게 문제가 될까요? 우리의 스크립트가 0이라는 이름의 파일을 처리하지 못한다는 사실을 견딜 수 있을까요...?

복잡한 경우들

위에서 본 매개변수 하나 또는 두 개의 경우보다 훨씬 더 복잡한 경우들이 많이 있습니다. 이런 경우에 여러분은 아마도 Getopt::Long 같은 도구를 사용하고 싶어질 것입니다. 여러분이 받아들기 원하는 매개변수의 종류를 선언하면 이 도구들은 그에 기반하여 @ARGV의 내용을 분석해 줍니다.

Otras páginas

Perl Tutorial in Korean

Author

Gabor Szabo (szabgab) Gabor Szabo