Perl을 호출하는 이식 가능한 방법

Perl을 호출하는 이식 가능한 방법

이것은에서 온 것입니다펄 프로그래밍, 제4판. 이것은 Perl 스크립트 실행에 관한 것입니다.

마지막으로, 불행하게도 #! 마법을 지원하지 않는 고대 유닉스 시스템을 사용하고 있다면. 행 또는 인터프리터 경로가 32자를 초과하는 경우(많은 시스템에서 기본 제공 제한) 다음과 같이 해결할 수 있습니다.

#!/bin/sh -- # perl, to stop looping
eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
if 0;

무슨 일이 일어나고 있는지 단계별로 설명해 주실 수 있나요? 나는 그것을 작동시키거나 포함시키려고 노력했지만 소용이 없었습니다.

위의 작업을 수행하면 다음과 같은 결과가 나타납니다.

/bin/sh: 0: Illegal option --

답변1

아이디어는 eval명령이 쉘과 Perl 모두에서 작동한다는 것입니다. 차이점은 개행이 쉘에서 명령을 종료하지만 Perl에서는 명령을 종료하지 않는다는 것입니다. 대신 Perl은 조건을 추가하는 다음 줄을 읽어서 if 0전체 명령을 효과적으로 무효화합니다.

next if $_ == 0(Perl은 이러한 "역방향" 구성에 대한 일부 단축형을 지원합니다. 예를 들어 대신 if ($_ == 0) { next }또는 print for @a대신 쓸 수 있습니다 for (@a) { print }.)

스크립트가 쉘에 의해 시작되면 쉘은 이를 처리하고 스크립트 이름( )과 해당 인수( )를 인수 eval로 제공하여 이를 Perl 해석기로 대체합니다 .$0$@

그런 다음 Perl은 실행하고, 읽고 eval, 건너뛰고(때문에 if 0) 스크립트의 나머지 부분을 진행합니다.


이것이 작동하는 방식입니다. 실제로 두 가지 때문에 오류가 발생합니다. 1) Perl이 hashbang 줄 자체를 읽는 것과 2) Linux가 hashbang 줄을 처리하는 방식입니다.

Perl이 해시뱅을 사용하여 스크립트를 실행할 때 실제로는 이를 주석으로 처리하지 않습니다. 대신에, 해시뱅에서 Perl에 주어진 모든 옵션(당신이 가질 수 있는 것 #!/usr/bin/perl -Wln등)을 해석할 것입니다. 그러나 또한 해석기를 확인하고 Perl에 의해 실행되어서는 안되는 경우 스크립트를 실행할 것입니다!

이 시도:

$ cat > hello.sh
#!/bin/bash
echo $BASH_VERSION 
$ perl hello.sh
4.4.12(1)-release

실제로 Bash를 실행합니다.

따라서 주석은 #perlPerl에게 예, 이것은 실제로 Perl에 의해 실행되어 쉘이 시작되지 않도록 해야 한다고 말하고 있습니다.다시.

그러나 Linux는 인터프리터 이름 뒤의 모든 항목을 단일 인수로 제공하므로 스크립트를 실행할 때 /bin/sh두 개의 인수 -- # perl, to stop looping및 로 실행됩니다 scriptname.pl. 첫 번째는 대시로 시작하지만, 그렇지 않기 --때문에 Dash와 Bash 모두 이를 옵션으로 해석하려고 합니다. 유효하지 않으므로 , bash ---또는 를 실행하려고 하는 것처럼 오류가 발생합니다 bash "-- #perl".

다른 시스템에서는 hashbang 라인에서 매개변수를 분리하면 -- #perl쉘이 이라는 이름의 명령을 찾으려고 시도하지만 --이는 작동하지 않습니다. 그러나 분명히 라인에서 기호를 주석 표시로 처리하거나 첫 번째 인수만 전달하는 일부 시스템이 있거나 있었습니다 . (바라보다#perl#perl##!이에 대한 Sven Mascheck의 페이지.) 이러한 시스템에서는 작동할 수 있습니다.


조금 더 좋은 직업이 있어요perlrun(적응):

#!/usr/bin/perl
eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
    if $running_under_some_shell;
print("Perl $^V\n");

이를 실행하면 bash ./script.pl실제로 Perl이 실행되고 (예를 들어) 인쇄됩니다 Perl v5.24.1. ( $running_under_some_shell기본적으로 falsy로 설정되는 정의되지 않은 변수입니다. if 0더 명확해지지만 설명이 덜 됩니다.)


귀하의 인용문에서 알 수 있듯이 이 모든 것은 #!제대로 작동하지 않는 고대 시스템에서만 필요합니다. 어떤 경우에는 전혀 지원되지 않으며 항상 셸에서 실행하려면 바이너리가 아닌 파일을 실행하는 셸이 필요합니다. 다른 경우에는 해시뱅 라인의 경로 길이가 제한되어 있으므로 #!/really/veeeery/long/path/to/perl작동하지 않습니다.

#!/usr/bin/perl최신 시스템에 해시뱅을 설치하기만 하면 됩니다 .

답변2

/bin/sh 수정: 0: 잘못된 옵션 --

오류를 피하는 가장 간단한 해결책은하나(일부 시스템에서) 호출에 대한 인수는 Perl 호출 [d]sh 에 옵션을 추가하는 것으로 보입니다 . 스크립트를 호출하는 데 사용되는 시스템, 커널, 쉘 및 Perl에 따라 다르기 때문에 보인다고 말합니다.-x

따라서 다음과 같은 것을 사용하는 것이 좋습니다.

#!/bin/sh --
eval 'exec perl -x -S $0 ${1+"$@"}'

#!/usr/bin/perl
print("Perl $^V\n");

위 스크립트에서 Perl이 실행하는 유일한 명령은 버전을 인쇄하는 것입니다.

짧은 소개

그러한 콘텐츠를 사용하는 유일한 이유는 책(21페이지)에 설명되어 있습니다.

마지막으로, 불행하게도 #! 마법을 지원하지 않는 고대 유닉스 시스템을 사용하고 있다면. 행 또는 통역사 경로가 32자를 초과하는 경우(많은 시스템에서 기본 제공되는 제한) 문제를 해결할 수 있습니다.

이것은 호출을 대체하는 보기 흉하고 복잡한 방법입니다 #!/usr/bin/perl.

같은 페이지 21에는 귀하가 게시한 스크립트가 있습니다. 이 문제에 대한 다음 스크립트는 577페이지에 있습니다(계속 읽기).

perl참고: 스크립트를 언제든지 호출하려면 첫 번째 줄에 이름(단어)만 포함하면 됩니다 perl ./script. 이 경우 이 단어는 Perl의 반복을 방지합니다. 사실은,두번째책(577페이지)의 예는 다음과 같습니다.

#!/bin/sh -- # -*- perl -*- -p
eval 'exec perl -S $0 ${1+"$@"}'
if 0;

안에(# -- 진주 -- -p)는 책에서 Perl에 의해 해석되는 것으로 설명됩니다(그리고 대부분 무시됩니다).

그러나 이는 쉘 호출이 #!/bin/sh여러 개의 ( ) 인수를 허용한다고 가정합니다 --. 일부 시스템에서는 그렇지 않습니다. 다음 줄로 이동하거나(emacs에서 요구하는 경우) 삭제하는 것이 더 좋습니다:

#!/bin/sh -- # 
# -*- perl -*- -p
eval 'exec perl -S $0 ${1+"$@"}'
if 0;

Perl을 호출하는 이식 가능한 방법

기본 사항을 이해하기 위해 가장 먼저 이해해야 할 것은 스크립트가 두 번 처리될 수 있다는 개념입니다. 한 번은 셸에 의해 처리되고 그 다음에는 셸에 의해 호출된 인터프리터(이 경우 Perl)에 의해 처리됩니다.

스크립트는 sh script, bash ./script, 간단히 ./script, 또는 경로에 현재 작업 디렉터리가 포함된 경우 , script또는 , /usr/bin/perl script또는 처럼 호출될 수 있다는 점을 이해하세요 perl script. DOS, 유닉스, 윈도우, 리눅스 등에서

모든 시스템에서 작동하는 Perl을 호출하는 방법은 무엇입니까?

쉘 프로세스

이 코드(cat ./script):

#!/bin/sh -- 
eval 'exec perl -S $0 ${1+"$@"}'
if 0;

./script거의 모든 쉘( 또는 shell ./script다음과 같이 호출됨)로 해석될 수 있습니다 .

  1. 커널은 첫 번째 줄을 읽고 이를 shebang으로 인식한 다음 /bin/sh파일(또는 링크)에 있는 셸 실행 파일을 시작합니다. 또한 커널은 --"옵션의 끝"을 나타내는 인수로 [a] [b]를 수락하고 처리합니다 . 그건:

    #!/bin/sh --
    
  2. 쉘(이 있다고 가정 /bin/sh)은 첫 번째 라인(주석 처리되지 않음)을 읽고 실행합니다.

    eval 'exec perl -S $0 ${1+"$@"}'
    

    첫 번째 단어(eval)는 삭제를 수행하기 위해 인수 목록을 명령줄로 변환합니다.하나참조 레이어. 즉, 다음과 같은 명령줄로 변환됩니다.

    exec perl -S $0 ${1+"$@"}
    

    이 (새) 명령줄의 첫 번째 단어(예: )는 실행 중인 프로세스를 실행 파일(PATH에서 검색)과 명령줄의 나머지 부분을 확장하여 생성된 인수로 바꾸도록 exec커널에 지시합니다 .perl

첫 번째 인수( )는 Perl에게 다음 인수( )에 지정된 실행 가능한 스크립트 -S도 검색하도록 지시합니다 . $0인수는 (보통) 이전에 로드한 셸 스크립트(게시한 코드가 포함된 스크립트라고도 함 ) $0의 이름입니다 ../script

다음 인수: ${1+"$@"}의미: arg가 $1존재하고 null( )이 아닌 경우 ${1+...}전체 인수를 확장된 결과로 바꿉니다 "$@". 확장은 "$@"목록입니다.모두스크립트의 매개변수를 로드합니다.

다음 명령줄에 제공된 매개변수를 확인하기 위해 테스트할 수 있습니다.

    #!/bin/sh -
    eval 'exec /usr/bin/printf "<%s> " -S $0 ${1+"$@"}'

다음 스크립트를 호출하세요.

    $  ./script one two t33 f44 "f55 s66"
    <-S> <./script> <one> <two> <t33> <f44> <f55 s66>

따라서 원본 스크립트의 이 줄은 perl(동등한)을 호출하여 다음을 수행합니다.

    $ perl -S ./script one two t33 f44 "f55 s66"

Perl에 대한 호출은 실행 중인 쉘 스크립트(exec)를 대체합니다.

펄 측

그런 다음 Perl은 스크립트를 로드하고 해당 내용을 재해석합니다.
Perl의 주석은 다음으로 시작합니다.#
따라서 첫 번째 줄(shebang)은 Perl에 대한 주석입니다.

두 번째 줄: 다음 줄이 다음 줄이면 eval 'exec perl -S $0 ${1+"$@"}'perl로 해석됩니다(이것은 eval유효한 Perl 명령입니다).if 0;아니요거기. Perl의 세 번째 줄아니요두 번째 줄을 실행합니다. 매우 긴 메소드를 작성하십시오 nop.

나머지 스크립트는 Perl에 의해 해석되고 실행됩니다.

이러한 구성은 모두 매우 복잡한 작성 방법입니다.

#!/usr/bin/perl

왜 그것이 정말로 필요한가?

경로 설정

"Hello World"의 예~처럼-xPerl 옵션을 다음과 같이 사용하십시오 .일반적으로 perldoc 웹사이트에서 권장됩니다..

Perl에 -x 호출을 추가하면 Perl 자체가 #!/usr/bin/perlShebang(첫 번째 줄 아래에 여러 줄)을 검색하여 스크립트 실행을 시작하게 됩니다. 이것도 중복되는 -x것처럼 보입니다 if 0.

#!/bin/sh -
eval 'PATH="/usr/bin:/bin:/usr/local/bin:$PATH";: \
;exec perl -x -S -- "$0" ${1+"$@"};#'if 0;

#!/bin/perl -w
# Above is magic header ... real Perl code begins here
use strict;
use warnings;
print "hello world!\n";

이는 Perl 실행 파일을 검색하기 위한 보다 이식 가능한 경로를 설정합니다. 필요한 경우 Perl에 대한 호출을 보다 안정적으로 만들기 위해 더 많은 변수를 설정할 수 있습니다.

[d] perl 옵션은 -xperl:이라는 이름의 shebang 행을 검색하여 #!/bin/perl -wperl에서 스크립트 해석을 시작합니다. 이는 쉘과 펄의 차이를 더 명확하게 만듭니다.

스크립트가 다음과 같이 호출되는 경우:

$ ./script
hello world!

$ sh ./script
hello world!

$ perl ./script
hello world!

그러나 csh쉘에서 호출하면 실패합니다. 이 문제는 eval '(exit $?0)'테스트와 새 실행자를 추가하여 해결할 수 있습니다.

#!/bin/sh --
eval '(exit $?0)' && eval 'PATH="/usr/bin:/bin:/usr/local/bin:$PATH";: \
     ;exec perl -x -S -- "$0" ${1+"$@"};#'if 0;
exec 'exec perl -x -S -- "$0" $argv:q;#'.q

Perl 스크립트 실행을 위한 매직 헤더
관련된 긴 설명입니다.

[a] 뒤에 오는 전체 문자열은 #!/bin/sh인수가 하나만 있는 것으로 해석될 수 있습니다. 이 경우 전체 문자열이 -- # perl, to stop looping오류로 보고될 수 있습니다. bash가 실행될 때 스크립트를 직접 로드하는 것과 마찬가지로 ./script(실행 중인 쉘은 bash입니다). 자세한 내용을 읽어보세요"분할 매개변수"를 검색하세요..

[b] 드문 경우지만 a를 사용하는 것이 유용합니다. as 대신 --a를 사용하는 것이 이식성이 더 좋습니다.-이 질문에 설명되어 있습니다.

관련 정보