[첫화면으로]Perl/System

마지막으로 [b]

5 번째 수정본
(5 번째 수정본부터 5 번째 수정본까지의 변경사항) (소소한 수정)
(두 수정본의 내용이 동일하거나, 수정본을 비교할 수 없음.)
1. 번역: 펄의 system() 함수 사용하기 ( Using the Perl system() function )
1.1. system() 기초
1.2. system() 함수의 두 번째 형태
1.3. system() 함수의 세 번째 형태
1.4. system() 함수는 정확히 무엇을 반환하는가?
1.5. 특별히 주의해야 할 점
1.6. 개인적인 스타일 취향
1.7. system() 테마의 변주
2. 기타 & comments

1. 번역: 펄의 system() 함수 사용하기 ( Using the Perl system() function )

(원문: [Using the Perl system() function])

여러 매뉴얼들에서 펄의 system() 함수를 조금씩 다르게 설명하며, 중요한 부분을 빼먹곤 한다. 이 글은 내가 직접 펄 system() 함수를 문서화하고 추가로 몇 가지 용례와 잘못된 사용 예를 추가하려고 만들었다.

1.1. system() 기초

system() 함수는 자식 프로세스를 생성하고(fork) 운영 체제 명령문을 실행한 후, 자식 프로세스가 완료될 때까지 대기하고, 일종의 자식 프로세스 종료 상태값을 반환하여 여러분이 검사할 수 있도록 한다.

system() 함수의 인자는 세 가지 형태로 주어질 수 있다 (괄호는 생략 가능하다):

1. system($STRING);
2. system($PROGRAM, @ARGUMENT_LIST);
3. system { $PROGRAM } $FAUX_PROGRAM, @ARGUMENT_LIST;

첫 번째 형태가 가장 흔히 사용된다. 여러분이 어느 형태를 쓸지(또는 펄이 진짜로 사용할지)는 실행하려는 명령문에 셸 메타캐릭터가 들어있는지에 따라 결정된다. 셸 메타캐릭터는 다음과 같은 파이프, 리다이렉트, 구문 구분자 등을 포함한다: ( ps -aux | column 1 > pids ); date 정확히는 다음 문자들이다:

 & ; ` ' \ " | * ? ~ < > ^ ( ) [ ] { } $ \n \r

만일 여러분이 실행하려는 명령문에 저 문자들 중 일부가 포함되어 있다면, 첫 번째 형태를 사용하여야 한다. 예를 들면:

system("$tace < $tcmd > $tout");

이 경우 하나의 문자열 인자가 커맨드 셸에 전달된다.

여기서 기억해야 할 중요한 점 하나는, 일단 system() 함수를 사용했다면 여러분이 만든 스크립트는 더 이상 이식성이 있지(portable) 않을 것이라는 점이다. 윈도우와 유닉스 시스템을 오가며 사용하려면 적당히 고쳐야 할 것이다.

그런데 어떤 셸을 쓰는가?

유닉스 같은 운영체제에는 여러 가지 커맨드 셸이 있다: sh, csh, tcsh, bash, zsh 등등. 이 중 어느 것이 사용되는가? /etc/passwd 파일에 정의되어 있는, 이 펄 스크립트를 실행한 사용자의 기본 셸이 사용된다. 여기서 문제의 소지가 있는데, 만일 여러분이 한 명의 사용자로서 스크립트를 개발하였고 이 스크립트를 다른 사용자에게 넘겨주거나, 'root' 등 다른 사용자의 자격으로 시스템 cron 작업으로 등록했다면, system() 함수가 여러분이 사용했던 셸이 아닌 다른 셸에서 실행될 수 있다는 점이다. 셸 내장 명령어의 종류나 $PATH 변수에 등록된 명령어들의 순서가 여러분이 스크립트를 만들 때와 다를 수 있다는 것이다.

여러분이 원하는 셸을 사용하도록 추가적인 셸 실행 계층을 넣어서 이런 문제의 소지를 최소화할 수 있다.

system("/bin/tcsh -c 'diff file file.old > file.diff 2>&1 /dev/null'");

하지만 더 나은 접근법은 이런 것이다:

1. 가능하면 모든 유닉스 셸에 공통적으로 사용되는 셸 연산자들만 사용한다(단순한 리다이렉션, 파이프 등).

2. 언제나 실행하려는 운영체제 명령어의 전체 경로명을 적는다. (이러면 다른 유닉스 시스템으로의 이식성이 떨어지지만, 이것은 보안과 관련하여 양보해야 할 중요한 문제이다.)

다음과 같이 하지 말라:

system("chown $user.sgd newdata.txt");

다음과 같이 하라:

system("/usr/bin/chown $user:sgd newdata.txt");
system("/usr/ucb/chown $user.sgd newdata.txt");

1.2. system() 함수의 두 번째 형태

system() 함수의 두 번째 형태에서는:

system($PROGRAM, @ARGUMENT_LIST);

셸 메타캐릭터 체크가 이루어지지 않는다. 여러분은 단순히 명령어에 인자 목록을 주어 실행하여야 한다:

system('/usr/bin/cp', $from, $to);

이것은 범용적인 문자열 인자 형태의 system()과 비교했을 때 두 가지 측면에서 효율적일 수 있다. 첫째로, 이 형태는 명령어를 실행하기 위해 셸을 띄우지 않고, 유닉스의 execvp 시스템 콜을 써서 직접 실행한다. 따라서 런타임에 프로세스 오버헤드가 줄어든다. 둘째로, 컴파일타임과 런타임에 인자들을 하나의 문자열로 만들기 위해 문자열 결합을 할 필요가 없다.

펄은 더 빠른 쪽을 선택한다

만일 첫 번째 형태를 사용하였는데 인자에 셸 메타캐릭터가 전혀 들어있지 않다면, 펄은 여러분이 인자로 준 문자열을 공백 문자를 기준으로 분리한 후 두 번째 형태로 바꾸어 실행한다. 이렇게 함으로써 추가로 셸을 띄우지 않도록 한다.

system('mv /etc/group_comb /etc/group') => system('mv', '/etc/group_comb', '/etc/group')

따라서 여러분은 두 번째 형태를 쓰려고 고민할 필요가 없다. 펄이 알아서 하게 두라.

1.3. system() 함수의 세 번째 형태

system() 함수의 세 번째 형태는 다음과 같다:

system { $PROGRAM } $FAUX_PROGRAM, @ARGUMENT_LIST

이것은 두 번째 형태의 변형으로, 펄의 간접 객체 표기 문법을 사용한 것이다. 이렇게 하면 실제 실행되는 명령어와, 그 명령어를 'ps'나 'top' 같은 프로그램을 써서 프로세스 테이블을 들여다볼 때 나타나는 이름을 서로 다르게 할 수 있다:

system { '/bin/sleep' } 'sleeping', 1000;

이 형태를 써먹을 기회는 드물지만, 이 형태가 유용할 수 있는 특별한 케이스가 있다:

system { $args[0] } @args;

이것은 @args 배열의 원소가 여러 개이든, 아니면 한 개뿐이고 그 안에 셸 메타캐릭터가 들어있든 상관없이 강제로 system() 함수가 execvp를 사용하는 형태로 실행되도록 한다.

주인장 주: 예를 들어서,

system { 'ls | less' } 'LISTING';

위와 같이 파이프를 쓰더라도 셸을 따로 띄우지 않고 직접 ls | less라는 이름의 파일을 찾아서 실행하려고 한다는 것이다. 물론 그 실행은 실패한다.

1.4. system() 함수는 정확히 무엇을 반환하는가?

1.5. 특별히 주의해야 할 점

1.6. 개인적인 스타일 취향

1.7. system() 테마의 변주

2. 기타 & comments

이름:  
Homepage:
내용:
 

컴퓨터분류

이 수정본 편집일: 2016-2-4 6:43 pm (변경사항 [d])
4459 hits | Permalink | 변경내역 보기 [h] | 현재 수정본 보기 | 5 번째 수정본 소스 보기