이것은에서 온 것입니다펄 프로그래밍, 제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를 실행합니다.
따라서 주석은 #perl
Perl에게 예, 이것은 실제로 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
다음과 같이 호출됨)로 해석될 수 있습니다 .
커널은 첫 번째 줄을 읽고 이를 shebang으로 인식한 다음
/bin/sh
파일(또는 링크)에 있는 셸 실행 파일을 시작합니다. 또한 커널은--
"옵션의 끝"을 나타내는 인수로 [a] [b]를 수락하고 처리합니다 . 그건:#!/bin/sh --
쉘(이 있다고 가정
/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"의 예~처럼-x
Perl 옵션을 다음과 같이 사용하십시오 .일반적으로 perldoc 웹사이트에서 권장됩니다..
Perl에 -x 호출을 추가하면 Perl 자체가 #!/usr/bin/perl
Shebang(첫 번째 줄 아래에 여러 줄)을 검색하여 스크립트 실행을 시작하게 됩니다. 이것도 중복되는 -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 옵션은 -x
perl:이라는 이름의 shebang 행을 검색하여 #!/bin/perl -w
perl에서 스크립트 해석을 시작합니다. 이는 쉘과 펄의 차이를 더 명확하게 만듭니다.
스크립트가 다음과 같이 호출되는 경우:
$ ./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를 사용하는 것이 이식성이 더 좋습니다.-
이 질문에 설명되어 있습니다.