3-2.A Magic Header for Starting Perl Scripts 日本語訳 のバックアップの現在との差分(No.1)


  • 追加された行はこの色です。
  • 削除された行はこの色です。
A Magic Header for Starting Perl Scripts 日本語訳

原文リンク
https://www.google.co.jp/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=0ahUKEwj5_s2jlqHLAhXMm5QKHYEsA9IQFggbMAA&url=http%3A%2F%2Fcollaboration.cmc.ec.gc.ca%2Fscience%2Frpn%2Fbiblio%2Fddj%2FWebsite%2Farticles%2FTPJ%2F2003%2F0304%2F0304c%2F0304c.htm&usg=AFQjCNEOxR1Jbpz5KK1es4B_iAsiyqEYwA
原文リンク:http://collaboration.cmc.ec.gc.ca/science/rpn/biblio/ddj/Website/articles/TPJ/2003/0304/0304c/0304c.htm



The Perl Journal 2003年4月
Péter Szabóによる

Péterはハンガリーのブダペスト技術経済大学でコンピュータ科学と情報科学を研究しています。 彼の主な関心はプログラミング言語、モデル検証、およびコンピュータ組版をです。 彼と[email protected]でコンタクトすることができます。
Péterはハンガリーのブダペスト技術経済大学でコンピュータ科学と情報科学を研究しています。 彼の主な関心はプログラミング言語、モデル検証、およびコンピュータ組版です。 彼と[email protected]でコンタクトすることができます。
----
ほとんどのPerlスクリプトは、#!とワード'''perl'''(例えば、#!/usr/local/bin/perl -w)を含む行で始まります。 この行は、Unixオペレーティングシステムに、現在のファイルをバイナリ実行可能ファイルとして扱うのではなく、記述された'''perl'''インタプリタを記述されたオプションと共に起動し、そのインタプリタがそのPerlスクリプトを取り扱うように指示しています。 この最初のー行の概念は単純ですが、それを普遍的に適用できるように書くのはとても困難になりがちです。

ほとんどのPerlスクリプトは、#!と世界のperl(例えば、#!/ usr / local / bin / perlの-w)を含有するで始まる行で始まります。 この行は、バイナリ実行可能ファイルとして現在のファイルを扱うべきではないUNIXオペレーティング・システムに指示しますが、それは指定されたオプションで指定されたperlインタプリタを起動する必要があり、そのインタプリタがPerlスクリプトの世話をします。 この最初の行の概念は単純であるが、それは普遍的に適用できるように書くのは非常に困難になる傾向があります。
たとえば、'''#! /usr/local/bin/perl -w'''を記述することは、'''perl'''が'''/usr/bin'''にある多くの、おまかせ設定済みLinuxマシン上では、'''my_script.pl: No such file or directory'''という紛らわしいメッセージの原因となります。'''#! /usr/bin/perl -w'''と記述することそんなLinuxマシン上の問題を解決はしますが、'''perl'''が標準の置き場所が'''/usr/local/bin'''である多くのSolarisシステムとの互換性を破壊します。#!の後で記述するべき標準的な'''perl'''の場所は無いという悪いニュースがあります。さらに悪いことに、いくつかの古いUnixシステムは#!行の記述に非常に厳格なルールを課します。結局、以下から選択することになります。
-ユーザーベースの大多数のために動くパスを記述します。
-非互換性を文書化し、問題がある場合は、丁重に手動でスクリプトを変更するようユーザーにお願いします。
-変更を自動化するインストールスクリプトを書きます。 (どのようにユーザーがインストールスクリプトを開始するのか?'''perl install.pl'''と入力することで、かな。)
-どこでも動作するソリューションを見つけます。 

たとえば、#を指定します! / usr / local / bin / perlの-w混乱my_script.pl原因となります。perl が / usr / bin /に置かれている多くのアウトオブボックスのLinuxシステム上でそのようなファイルやディレクトリのメッセージを。 #を指定します! / usr / binに/ perlの-wは、これらの Linuxマシン上で問題を解決しますが、perlのための標準的な場所は/ usr / local / bin / perlのである上に、ほとんどのSolarisシステムとの互換性を中断します。 悪いニュースは、#の後に指定するperlのための標準的な場所がないことです!。 さらに悪いことに、一部の古いUNIXシステムは#!行に指定することができますどのように非常に厳格なルールを課します。 その結果、あなたは間に選択することができます:
この記事では最後のオプション-マジックPerlヘッダー、任意のUNIXオペレーティング・システム上でPerlスクリプトを起動するための複数行のソリューションについて説明します。

    ユーザーベースの大多数のために働くのパスを指定します。
    非互換性を文書化し、丁寧に問題がある場合は、手動でスクリプトを変更するようユーザーに尋ねます。
    変更を自動化するインストールスクリプトを書きます。 (どのようにユーザーがインストールスクリプトを開始します?perlのinstall.plを入力すると、おそらく。)
    どこでも動作するソリューションを見つけます。 
***一般的なワンラインの落とし穴 [#ke44fd16]

この記事では最後のオプション・マジックPerlのヘッダー、任意のUNIXオペレーティング・システム上でPerlスクリプトを起動するための複数行のソリューションについて説明します。
一般的なワンライン落とし穴
最も露骨な#!ワンライナーも、どうもいまひとつです。
-'''#!/usr/local/bin/perl -w -T'''の記述は、動作しません。なぜなら、いくつかのUNIXオペレーティングシステムでは'''#!'''の後は一つのコマンドラインスイッチのみ許されています。このワンライナーは、Linux上で動作しません。
-'''#!/usr/local/bin/perl -wT'''の記述は、動作しません。なぜなら、いくつかのUNIXオペレーティングシステムでは'''#!'''の後にスペースが求められています。このワンライナーは、/user/local/binが存在する場合のみLinux上で動作します。(ただし、普通はない)
-'''#! /usr/local/bin/perl -wT'''の記述は、動作しません。なぜなら、'''perl'''は恐らく/usr/bin/とか他のどこかに置かれているだろうからです、Debian Linuxのように。(要確認:あなたのスクリプトのユーザーはperlバイナリの場所を見つけることができたり、スクリプトの最初の行を適切に変更するのに十分な教育を受けていないかもしれません。またあるセキュリティの設定上では、たとえ彼らが正確な変更のし方を知っていたとしてもそれをする許可は持っていないかもしれません)このワンライナーは、おまかせ設定Linux上ではまれにしか動きません。
-'''#! perl -wT'''の記述は、動作しません。なぜならいくつかのUNIXシステムは、#!の後は(/からはじまる)絶対パス付の動作可能ファイル名が求められています。このワンライナーはLinux上では動きません。
-'''#! /usr/bin/env perl -wT'''の記述は、動作しません。なぜならいくつかのシステムはコマンド名の後にはゼロかひとつのアーギュメントしか許されません。(もっと言えば、いくつかのシステム上では最初の行の長さには制限があり、それは32文字あるいは64文字以下です。)コマンドライン以外の場所で'''-T'''スイッチを記述するのは非常に難しいでしょう。('''-w'''スイッチの方はより易しいです:Perlコードの前に'''BEGIN{$^W=1}'''と書くだけです。'''-T'''スイッチはセキュリティスイッチであり、その記述が遅いと、悪意のある事故のバックドアを開けることになります。あなた(プログラマー)はここはとりわけ注意しなければなりませんが、正しいスイッチを記述する場所がないので、難しいです。このワンライナーはLinuxでは動きません。
-'''#! /usr/bin/env perl'''の記述は、いずれにしろ動作しません。なぜなら'''env'''はいくつかのシステムでは無かったり別のところに置かれているからです。このワンライナーはLinux上では動きます。

!最も明白な#ワンライナーだけで十分ではありません。
***マジックPerlヘッダーを作る [#re8fd208]

    指定#!/ usr / local / bin / perlの-w -TいくつかのUNIXオペレーティング・システムは、#の後にのみ、単一のコマンドラインスイッチを可能にするので動作しません!。 このワンライナーは、Linux上で動作しません。
    いくつかのUNIXオペレーティング・システムは、#の後にスペースを期待するため、#!/ usr / local / bin / perlの-WTを指定すると、動作しません!。 このワンライナーは/ usr / local / bin / perlが存在する場合にのみLinux上で動作します(ただし、通常はしません)。
    #を指定します! / usr / local / bin / perlの-WT perlはそのようなDebianのLinux上のように、 は/ usr / binにまたはどこか他の場所に存在する可能性がありますので、動作しません。 (注意:スクリプトのユーザーはperlのバイナリを見つけて、それに応じて、スクリプトの最初の行を変更することができることは十分に教育を受けられない場合があり、そしていくつかのセキュリティ構成では、彼らはそれを行うためのアクセス許可を持っていない可能性があり、場合でも、彼らは変更するには、正確に何を知っています。)このワンライナーはほとんどアウトオブボックスのLinux上で動作しません。
    #を指定します! 一部のUNIXオペレーティング・システムでは、#の後に絶対的な実行可能ファイル名(/で始まる)を期待するためのperl -WTは動作しません!。 このワンライナーは、Linux上では動作しません。
    #を指定します! / usr / binに/ envをperlのいくつかのシステムでは、コマンド名の後にのみ、ゼロまたは1つの引数を可能にするので-WTは動作しません。 (また、いくつかのシステムでは、最初の全体の長さには限界がある行は、それが32または64文字限り少ないことができます。)コマンドライン以外の場所からの-Tスイッチを指定することは非常に難しいだろう。 (-wスイッチが容易である:ちょうどBEGIN書き込み{W $ ^ = 1} Perlコードの前に)-Tスイッチは、セキュリティスイッチであり、遅すぎる、それを指定すると、悪質な事故のためにバックドアを開きます。 あなた(プログラマ)はここに非常に注意する必要がありますが、正しいスイッチを指定する場所がないので、それは困難です。 このワンライナーは、Linux上では動作しません。
    #を指定します! ENVが見つからないか、いくつかのシステム上の他の場所に配置される可能性があるために/ usr / binに/のenv perlは 、いずれか、動作しません。 このワンライナーは、Linux上で動作します。 
#!の単一行で一般的なケースの問題を解決する方法が無いのは明らかです。なぜならスクリプトを走らせるためにPerlを起動する移植可能な方法がないからです。複数行での解決策が必要になるでしょう。このセクションではこの解決策を作り始めます。この先、私は問題点や制限を特定し、次の章では、最終的な、どんなUnixシステム上でもPerlスクリプトが動作する、完全な、マジックPerlヘッダーを提示します。

マジックPerlのヘッダーを構築
スクリプトの、唯一の移植可能なはじめかたは、
 #! /bin/sh
です。
'''/bin/sh'''は全てのUNIXシステム上で使えます。しかし、それはBourneシェルバリアント(Bashとかash)、Kornシェルバリアント(pdkshとかzsh)そしてCシェルバリアント(cshやtcsh)を含む他のシェルへのシンボリックリンクであるかも知れません。多くのUNIXユーティリティと、(ANSI C, POSIX.2, BSD4.3と適合する)libc '''system(3)'''関数は'''/bin/sh'''の動作を当てにしています。だから、'''/bin/sh'''が存在し、Bourneシェル、Kornシェル、またはCシェルの変種であると仮定することは、かなり合理的です。Linuxでは、'''/bin/sh'''は通常'''/bin/bash'''へのシンボリックリンクです。(Linuxのインストールディスクでは、時々'''/bin/ash'''へのシンボリックリンクであったり、BusyBoxの内蔵ashへのシンボリックリンクです)Win32 MinGW MSYSでは、'''/bin/sh'''はBashですが、/bin/bashはありません。Solarisでは、'''/bin/sh'''はSun独自の、単純化したBourneシェルクーロンですし、Digital UNIXも'''/bin/sh'''のなかは簡単なBourneシェルクーロンです。(多くのシェルスクリプト中に見受けられる'''#!/bin/sh --'''行は、実行可能のために任意のファイル名を許容しますが、ここでは動きません。なぜなら、tcshは--スイッチにエラーを出します。)

スクリプトを実行するPerlを起動するには、noポータブルな方法がないため問題に対するシングル#!-lineソリューションは、一般的なケースでは存在しないことは明らかです。 複数行の溶液が必要となります。 このセクションでは、私はこのソリューションを構築するために開始されます。 私は道に沿って問題と制限を特定し、次のセクションでは、あなたが任意のUNIXシステム上でPerlスクリプトを起動することができます最終的に、完全な魔法のヘッダーを提示します。
私たちは、$PATH中で'''perl'''の実行ファイルを検索し、正しいスイッチとともにそれを実行する簡単なシェルラッパーを書くことができます。 実際、これは、.batバッチファイルを使ったWin32システム上で動作する唯一の方法です。 一つの解決策の候補は、
 ## file my_script.sh, version 1
 #! /bin/sh
 perl my_script.pl

スクリプトのための唯一のポータブル始まりです。

  #!  / binに/ shを 

/ binに/ shが 、すべてのUNIXシステムで提供されていますが、それは、とCシェルの変種(例えばpdkshのとzshのような)Kornシェルの変種(例えば(例えばバッシュや灰など)Bourneシェルの変異体を含む、任意のシェルへのシンボリックリンクであるかもしれません)CSHやtcshのよう。 多くのUNIXユーティリティ、およびlibcのシステム(3)関数(ANSI C、POSIX.2、BSD 4.3に準拠)は、作業/ binに/ shのに依存しています。 だから、/ binに/ SHが存在し、Bourneシェル、Kornシェル、またはCシェルの変種であると仮定することは、かなり合理的です。 Linuxでは、/ binに/ shが 、通常は/ binに/ bashのへのシンボリックリンクです。 (Linuxのディスクをインストールするには、それは時々 ビン/灰/へのシンボリックリンクまたは内蔵BusyBoxのの灰である。)のWin32のMinGW MSYS上で、/ binに/ shが bashのです。しかし、/ binに/ bashはありません。 Solarisでは、/ binに/ shが Sunの独自の単純化のBourne shellのクローンで、デジタルUNIXでは、/ binに/ shの中で簡単なのBourneシェルのクローンを持っています。 (ライン#/ binに/ shを- !それはtcshの場合は、のためにエラーになりますので、実行可能ファイルの任意のファイル名はここでは動作しません可能にするために、多くのシェルスクリプトに見られる- 。スイッチ)

私たちは、$ PATHにperlの実行ファイルを検索し、正しいスイッチとそれを実行する簡単なシェルラッパーを書くことができます。 実際には、これは、これは.BATバッチファイルを使用して、Win32システム上で動作することが唯一の方法です。 解決のための候補者は次のとおりです。

  ##ファイルmy_script.sh、バージョン1#!  / binに/ shのperlのmy_script.pl ##ファイルmy_script.plの#実際のPerlコードはここから始まります 

 ## file my_script.pl
 # real Perl code begins here
です。
これは、以下のような問題があります。

1.これは、コマンドライン引数を渡すことはありません。

2.それは、 終了()ステータスを伝達しません。
2.それは、 '''exit()'''ステータスを伝達しません。

3.これは通常間違っている現在のディレクトリからそれを取るだろうし、またセキュリティ上の問題を提示する可能性があります-it の$ PATHにPerlスクリプトを見つけることができません。
3.これは'''$PATH'''のなかのPerlスクリプトを見つけることができません。カレントディレクトリからPerlスクリプトを取ってくるだろうし、それは通常間違いです。また、セキュリティ上の問題を提示する可能性があります。

4.これは、2つの別々のファイルを必要とします。

問題1-3は非常に簡単に克服することができます。

  ##ファイルmy_script.sh、バージョン2#!  / binに/ shをexecのperlの-S  -  my_script.plの "$ @" 
 ## file my_script.sh, version 2
 #! /bin/sh
 exec perl -S -- my_script.pl "$@"

(このようなGNUバッシュ、灰、zshの、pdkshの、およびSolaris / binに/ shのような)すべてのBourneシェルとKornシェルが正しくmy_script.shを解釈することができます。 しかし、Cシェルは「変更されていない、シェルに渡されるすべての引数。 "のための異なる表記を使用します Q の代わりに"$ @を":彼らは$ ARGVを使用しています。 perlrun(1)マニュアルページがCシェルを検出し、思い出に残る構造物を説明します。
すべてのBourneシェルとKornシェル(例えば、Gnu Bash、ash, zsh, pdksh, Solarisの'''/bin/sh'''など)は'''my_script.sh'''を正しく解釈することができます。 しかしながら、Cシェルは「変更されていない、シェルに渡されるすべての引数"のための異なる書き方を使っています。'''"@"'''の代わりに'''$argv:q'''を使うのです。'''perlrun(1)'''のマニュアルページには、Cシェルを検出する印象的な構築物が記述されてます。
 eval '(exit $?0)' && eval 'echo "Korn and Bourne"'
 echo All
'''"All"'''というメッセージは、全ての3つのシェルタイプで出力されますが、KornシェルとBourneシェルの場合のみ'''"Korn and Bourne"'''メッセージを出力します。(zshでは、結果は'''$?'''の値に依ります。しかしこれは問題を起こしません。zshは我々の使うcshとBourneシェルの構造を両方理解するからです。)ここでのトリックは、'''$?'''は前コマンドの終了ステータスで、初期値0ですが、Cシェルでの'''$?0'''は変数'''$0'''が存在するために"1"を返すテストであるということです。

  evalを '(終了$?0')&&はeval "エコー" KornシェルとBourneシェル " 'エコーすべて 
Cシェル検出コードの'''echo'''を'''exec perl'''に変えることができて、それで終わりです。

メッセージには、「 すべて 」すべての3つのシェルのタイプにエコーされますが、唯一のKornシェルとBourneシェルシェルは「Kornシェルとボーン 」のメッセージを印刷します。 (zshのでは、結果は$?の値に依存しますが、zshのは、我々が使用CSHとBourneシェル構文の両方を理解するので、それは問題は発生しません。)ここでのトリックは$は?前回の終了ステータスであるということですCシェルの初期値が0の場合、しかし、$?0のコマンドは、変数$ 0が存在するため、「1」を返しますテストです。
 ## file my_script.sh, version 3
 #! /bin/sh
 eval '(exit $?0)' && exec perl -S — "$0" "$@"
 exec perl -S -- "$0" $argv:q

私たちは、perlのEXECコマンドへの Cシェル検出コードにエコーを変更することができますし、それはそれです。
今、私たちは最初の魔法のステップを作る準備ができています。'''my_script.pl'''とmy_script.sh'''を一つのファイルにつなげましょう。そのファイルは、shellから走らせると自らをperlを使って起動します。(ちょっとの間csh互換を忘れましょうーあとで戻ります)

  ##ファイルmy_script.sh、バージョン3#!  / binに/のshのeval '(終了$ 0≦)' &&のexecのperl -S  -  "$ 0"の "$ @"実行するperl -S  -  "$ 0" $のARGV:Q 

今、私たちは私たちのウィザードの最初の手順を行う準備ができている:シェルから実行したときにPerlを使用して自分自身を呼び出す単一のファイルの中にmy_script.plとmy_script.shを組み合わせます。 (後でそれに取得瞬間-we'llは、csh互換性を忘れます。)

単純な試みは次のようになります。

  #!  / binに/ shのevalの「エコーDEBUG。 幹部のperl -S $ 0 $ {1 + "$ @"} '0の場合。  #実際のPerlコードはここから始まります 
 #! /bin/sh
 eval 'echo DEBUG; exec perl -S $0 ${1+"$@"}'
 if 0;
 # real Perl code begins here

残念ながら、それは本当のPerlコードを実行されませんが、それはDEBUGメッセージの無限の数を生成します。 それはPerlが持っているためだビルトインハック:!最初の行が#で始まり、それが単語のperlが含まれていない場合、Perlはスクリプトを解析するのではなく、指定されたプログラムを実行します。 詳細についてはperlrun(1)マニュアルページの冒頭を参照してください。
残念ながら、これは本当のPerlコードを実行しません。数多くのDEBUGメッセージを生み出します。 それはPerlが持つビルトインハックのためです。:最初の行が#!ではじまり、'''perl'''という文字が無い場合、Perlはスクリプトを解析するのではなく、指定されたプログラムを実行します。 詳細については'''perlrun(1)'''マニュアルページの冒頭を参照してください。

次の簡単なトリックでは、(1)のマニュアルページはperlrunによって提案された、我々は最初の行にあるワードのperlが含まれています。
'''perlrun(1)'''マニュアルページで提案されている次の簡単なトリックでは、最初の行に'''perl'''という文字がが含まれています。

  #!  / binに/ shの - # -  *  -  perlの -  *  -  evalを 'execのperlの-S $ 0 $ {1 + "$ @"}' 0の場合。  #実際のPerlコードはここから始まります 
 #! /bin/sh — # -*- perl -*-
 eval 'exec perl -S $0 ${1+"$@"}'
 if 0;
 # real Perl code begins here

( - #* - *のperl - * - 、./my_script.pl / binに/ shを 、)、およびシェルが不快なエラー・メッセージが表示されますOSはコマンドラインを起動しますので、これは、Linuxを含む多くのシステム上で動作するように失敗します完全に偽のスイッチに関する。
これはLinuxを含む多くのシステム上で動作しません、なぜならOSはコマンドライン('''/bin/sh, --# *-* perl -*-, ./my_script.pl''')を起動し、そしてシェルは完全に偽りのスイッチに関する不快なエラーメッセージを出力します。

だから我々は最初の行を省略することができます。
そこで最初の行を省略することができて、

  0の場合はeval」のexec perlは$ 0 $ {1 + "$ @"}を-S ';  #実際のPerlコードはここから始まります 
 eval 'exec perl -S $0 ${1+"$@"}'
 if 0;
 # real Perl code begins here

このソリューションは、トーマス・エッサーのepstopdfユーティリティに触発され、perlのmy_script.plと./my_script.plの両方でLinuxシステム上で動作しているようです。 しかし、私たちはより良い行うことができます。 このスクリプト内の主要な欠陥は、オペレーティング・システムはスクリプトなどのASCII文字で始まる実行可能ファイルを認識し、/ binに/ shを介してそれらを実行しているという事実に依存しているということです。 いくつかのシステムでは、発生する可能性がある」、「バイナリファイル '' Execのフォーマットエラーまたは実行できません " '。
このソリューションは、トーマス・エッサーの'''epstopdf'''ユーティリティにインスパイアされています、そしてLinuxシステム上で、'''perl my_script.pl'''と'''./my_script.pl'''の両方で動作しているようです。 しかし私たちはもっとよくできます。このスクリプトの主な欠陥は、
「オペレーティング・システムはASCII文字で始まる実行可能ファイルをスクリプトであると認識し、'''/bin/sh'''を介してそれらを実行している」ということにたよっているということです。 いくつかのシステムでは、"Cannot execute binary file" とか "Exec format error" が発生する可能性があります。

最初の行はPerlとBourne互換シェルの両方に有効であるので、このスクリプトは非常にトリッキーであることに注意してください。 (これは、Cシェルでは動作しませんが、我々は後にその問題を解決します。)
このスクリプトは非常にトリッキーであることに注意してください。 なぜなら、最初の行はPerlとBourne互換シェルの両方に有効だからです。(これは、Cシェルでは動作しませんが、我々は後にその問題を解決します。)

解決策は、別の問題があります:誰かがスクリプトになどにスペースや他の変な文字、との奇妙なファイル名を与えた場合
このソリューションには、別の問題があります:スクリプトになどにスペースや他の変な文字との奇妙なファイル名を与えた場合、たとえば
 -e system(halt) 

  -eシステム(停止) 
すると、コマンド
 perl -S -e system(halt) 

その後、コマンド
は実行するが、ユーザーの'''$PATH'''上の'''halt'''という名前の危険なプログラムがある場合には、災害となる。この問題は簡単に解決可能で、シェルから'''$0'''を引用してそれに--を付加することで、Perlがその先のオプションを解釈させなくすればよい。

  perlの-S -eシステム(停止) 
----

ユーザーの$ PATH上の停止という名前の危険なプログラムがある場合には、災害である、実行されます。 さらにオプションの認識からPerlを防ぐために-この問題は、シェルから$ 0引用して、でそれを付けることによって、簡単に解決することができます。

!私たちは#ラインのための2つの相反する要求を持っている:ポータビリティの要件は、それが正確に#でなければならないということです! / binに/ shを 。 それは、前述の無限DEBUGループを回避するために、単語のperlが含まれている必要があります。 これらの要件の両方をsatisifyが、何のOSが最初に解析すると、Perlは第二を見つけるので、perlの-xを実行した後、2つの行を持っていることについてすることができます単一の行はありませんか?

  #!  / binに/のshのeval 'execをperlの-S -x  -  "$ 0" $ {1 + "$ @"}' 0の場合。  #!perlの-w#実際のPerlコードはここから始まります 
 #! /bin/sh
 eval 'exec perl -S -x — "$0" ${1+"$@"}'
 if 0;
 #!perl -w
 # real Perl code begins here

ここでのトリックは、-xスイッチで呼び出されたときにPerlが、#!にperlのすべてを無視するということです。 nonUNIXシステムのユーザは、perlの-xでこのスクリプトを呼び出す必要があります。 UNIXユーザーが自由に./my_script.pl、perlのmy_script.pl、perl の-x my_script.plのいずれかを選択し、さらにSH my_script.plことがあります。

このスクリプトの微妙なバイリンガルのトリックは、勉強の価値があります。 ファイルはperlの-xによって読み取られると、それはすぐに実際のPerlコードにスキップします。 ファイルがシェルによって読み込まれると、それはevalの行を実行します。それは、スクリプトのファイル名とコマンドライン引数でのperl -xを呼び出します 。 物事は、引数にスペースまたは引用符が含まれている場合でも動作するように、二重引用符と$ @は 、シェルスクリプトの妙技です。 -Sオプションは、ほとんどのシェルは(すなわち、$ 0は 、ユーザが入力したコマンドである)変わらない$ 0残すため、再び$ PATHにファイルを検索するためのPerlを伝えます。

2行目と3行目は有効なノーオペレーションPerlコードが含まれていますが、Perlは理由-xスイッチのこれらの行を解釈することはありません。 それはすぐに/ bin / shを起動しますので、これらの株はまた、完全にperlのmy_script.plによって無視されます。 しかし、 やる Perlでこのスクリプトを内蔵して、2行目と3行目は、コンパイルされ、解釈されますユーザー負荷は、無害なノーオペレーションコードが実行され、何の構文エラーが発生していない場合。

残る欠陥がまだあります。
-これは、Cシェルでは動作しません。 我々はすでに、このセクションでは、この以前に解決しています。
-これは、#と比較し、エラーメッセージに行番号を報告します!perlの-wライン。
-ロケール設定が無効である場合には、警告を出力します。 (醜い警告メッセージを表示するには、スクリプトを実行する前に、バッシュで「無効な輸出LANG = 'を設定してみてください。) 

    これは、Cシェルでは動作しません。 我々はすでに、このセクションでは、この以前に解決しています。
    これは、#と比較し、エラーメッセージに行番号を報告します!perlの-wライン。
    ロケール設定が無効である場合には、警告を出力します。 (醜い警告メッセージを表示するには、スクリプトを実行する前に、バッシュで「無効な輸出LANG = 'を設定してみてください。) 

行番号の問題に関しては、Perlを行う内蔵のは、このような構築物でスクリプトを再読み込みするために使用することができます。

  {BEGIN場合($のsecond_run!){$ second_run = 1;  ($ 0)を行います。  $ @ダイの場合の$ @; 出口 } } 
 BEGIN{ if(!$second_run){ $second_run=1; do($0); die $@ if $@; exit } }

BEGINは、ファイル全体をコンパイルすると、おそらく間違った行番号と構文エラーに文句からPerlを防ぐために、ここで必要とされます。 ダイの$ @ $ @を命令が正しく実行時 ​​エラーメッセージが出力されます場合 。 $ @をの詳細については、(1)perlvarを参照してください。 残念ながら、コード

  {BEGIN場合($のsecond_run!){$ second_run = 1;  ($ 0)を行います。  $ @ダイの場合の$ @; 出口は}} 42を死にます。 

 BEGIN{ if(!$second_run){ $second_run=1; do($0); die $@ if $@; exit } }
 die 42;
  
余分なエラーメッセージは「失敗したコンパイルは中止BEGIN。」が得られます ダイ42は、実行時 ​​エラーではなく、コンパイル時エラーが発生するため、このエラーが混乱しています。 メッセージを取り除くために、我々は何とか出口を排除し、}}の後に入力を解析し続けることはないPerlを伝える必要があります。 我々は十分に早く構文解析を停止する__END__トークンを使用します。

ロケールの警告が始まる複数行のメッセージである "perlの:警告:設定のロケールに失敗しました。」 環境変数LANG、LC_ALL、およびLC_ *で指定されたロケール設定が正しくない場合はPerlはこれを放出します。 (1)詳細についてはperllocaleを参照してください。 この警告のための本当の修正がインストールされ、正しくロケールを指定されています。 壊れたロケールがそれらに害を行いませんので、しかし、ほとんどのPerlスクリプトは、とにかくロケールを使用しないでください。

Perlはロケールの問題のための良好な診断ツールではありますが、ほとんどの時間は、我々は特にない、CGIで(これらの警告は、Webサーバのログファイルを埋めるだろう)、このような警告メッセージを望んでいない、またはプログラムがいくつかのシステムデーモンプロセスである場合、通常の操作では標準エラー出力への書き込みが禁止。 システム管理者は、実際にロケール設定を修正する必要がありますが、それは時間がかかることがあります。 ほとんどのユーザーは、とにかく、ロケールに依存しない単一のPerlスクリプトを実行するには数週間待機する時間を持っていません。

perllocale(1)のマニュアルページには、PERL_BADLANGは 、ロケールの警告を取り除くために真の値に設定する必要があることを述べています。 実際に、PERL_BADLANGは空でない、数値以外の文字列(例えば、PERL_BADLANG = 1が機能しない)に設定する必要があります。 だから我々は、シェルスクリプトのセクションで= xをPERL_BADLANGするように設定します。 Perlはシェルの前に呼び出された場合、これは効果がありませんので注意してください。 例えば、perlの 、perlの-x、perl の-S、およびperlの-x -SすべてのシェルがPERL_BADLANGを変えるチャンスを持っている長い前に警告を発します。
完成ヘッダー

***完成したヘッダ [#f24b67bd]

一緒にすべてを組み合わせることで、我々はマジックのPerlヘッダの最終版を持っています。 参照例1 。ファイルは、UNIXシステム上で実行可能な属性を持っている必要があります。

このヘッダーは、複数の言語で有効であるので、その意味は、インタプリタによって異なります。 幸いなことに、すべての通訳のヘッダの最終的な効果は、perlはヘッダの後、実際のPerlコードを実行して呼び出されるということです。 ヘッダがこれを実現する方法を見てみましょう:

    Perlで実行すると、-xスイッチを使用せずに、Perlは/ binに/すぐにshを実行します。 (/ binに/ shがシェルのいずれのタイプであってもよいです。)
    BourneシェルとKornシェルの変異体は、ファイルとして解釈します: 
-Perlで実行すると、-xスイッチを使用せずに、Perlは/ binに/すぐにshを実行します。 (/ binに/ shがシェルのいずれのタイプであってもよいです。)
-BourneシェルとKornシェルの変異体は、ファイルとして解釈します: 

      #!  / binに/ shを - 真&&のeval '...。 幹部のperl -T -x -S "$ 0"#{1 + "$ @"} '#コメントごみ 
 #! /bin/sh —
 true && eval '...; exec perl -T -x -S "$0" #{1+"$@"}' # comment
 garbage

    そこで、彼らはperlの-xを実行します。 

    Cシェルの変異体は、ファイルとして解釈します: 
-Cシェルの変異体は、ファイルとして解釈します: 

      #!  / binに/ shの - 偽&&のeval '...';  evalの '...。 幹部のperl -T -x -S  -  "$ 0" $ ARGV:Q '#コメントごみ 
 #! /bin/sh --
 false && eval '...' ;
 eval '...; exec perl -T -x -S — "$0" $argv:q'  # comment
 garbage
そこで、彼らはperlの-xを実行します。 

    そこで、彼らはperlの-xを実行します。 
-ヘッダーの2行目の最後のバックスラッシュは、余分のようですが、cshのは、バックスラッシュを付けずに文字列の真っ只中にラインの破壊を許可していないので、そうではありません。
-オペレーティング・システムは、/ binに/ shを 、いくつかのシェルの変形を実行して、ファイルを実行します。 これはあっても-hack!#について知っているが、ちょうどシェルスクリプトとしてASCIIファイルを扱わない古代のシステムのために真です。
-perlの-xは、ファイルを次のように解釈します 
 #!perl -w
 untaint $0; do $0; die $@ if $@; __END__
 garbage
だから、#!の行を尊重しないと、再び現在のファイルを実行します。 これは、エラーの行番号が正しく出てくるようにするとよいでしょう。 
-値をUNTAINTする唯一の方法は、正規表現の部分式のマッチングです。 私たちは=〜/(.*)/ S $ 0それを使用しています。
-$ 0扱いファイルなどの操作を行います。 
 eval 'garbage' if 0;
 eval 'garbage' . q+garbage+ if 0;
 # real Perl code
-$ 0がスクリプト(@INCを超えることの繰り返し処理)の位置については、$ ENV {PATH}を参照してくださいが、時間によって$ 0行いませんが呼び出されません 、$ 0はすでに$ ENV {PATH}の関連成分があれば、それの前に付けていパス検索が行われていたので、@INCには 、ここで検討されることはありません。 $ 0は相対パス名であってもよいが、これはパスサーチので、呼び出されなかったのchdir以来の問題()ではありません。 スクリプト内のインデックス機能がないと、@INCを見て、Perlは組み込みでftp.pl代わりに、私たちの魔法のスクリプトの現在のディレクトリにperlの-x ftp.pl呼び出すときftp.plという名前が分かっているだろうか 。
-($ 0行う呼び出し)前回のリードは__END__トークンでコンパイルを終了しているため、実際のPerlコードは一度だけコンパイルされます。 それは、単一引用符で囲まれた文字列のq +ガベージ+の一部であるため、実際のcompilaionは__END__トークンをバイパスします。
-コンパイルは#を無視する、内側行う発生するため、エラー行番号が正しく報告されています!。 )コンパイル時、マニュアルなどの実行時 ​​にエラーが、(死ぬことを呼び出し、両方のは、$ @文の場合、ダイの$ @によって早期に発見し、報告されています。 実際のPerlコードを一度にコンパイルされるので、各エラーは一度だけ報告されます。
-実際のコードは、任意の出口の数()、execを()、 フォーク()含まれており、(死ぬ)を呼び出し、期待どおりに動作します。サブルーチンの外に返すことがあります 。幸いなことに、純粋なPerlで許可されていませんので、我々は持っていませんこのケースを治療することができます。
-INC @プッシュは、「。」 のperl 5.8.0 -Tによって必要とされます。 

    ヘッダーの2行目の最後のバックスラッシュは、余分のようですが、cshのは、バックスラッシュを付けずに文字列の真っ只中にラインの破壊を許可していないので、そうではありません。
    オペレーティング・システムは、/ binに/ shを 、いくつかのシェルの変形を実行して、ファイルを実行します。 これはあっても-hack!#について知っているが、ちょうどシェルスクリプトとしてASCIIファイルを扱わない古代のシステムのために真です。
    perlの-xは、ファイルを次のように解釈します 

      #!perlの-w UNTAINT $ 0;  $ 0行います。  $ @ダイの場合の$ @;  __END__ごみ 

    だから、#!の行を尊重しないと、再び現在のファイルを実行します。 これは、エラーの行番号が正しく出てくるようにするとよいでしょう。 

    値をUNTAINTする唯一の方法は、正規表現の部分式のマッチングです。 私たちは=〜/(.*)/ S $ 0それを使用しています。
    $ 0扱いファイルなどの操作を行います。 

      0の場合はeval 'ごみ';  evalの「ごみ」。  Q +ゴミ+ 0の場合。  #実際のPerlコード 

    $ 0がスクリプト(@INCを超えることの繰り返し処理)の位置については、$ ENV {PATH}を参照してくださいが、時間によって$ 0行いませんが呼び出されません 、$ 0はすでに$ ENV {PATH}の関連成分があれば、それの前に付けていパス検索が行われていたので、@INCには 、ここで検討されることはありません。 $ 0は相対パス名であってもよいが、これはパスサーチので、呼び出されなかったのchdir以来の問題()ではありません。 スクリプト内のインデックス機能がないと、@INCを見て、Perlは組み込みでftp.pl代わりに、私たちの魔法のスクリプトの現在のディレクトリにperlの-x ftp.pl呼び出すときftp.plという名前が分かっているだろうか 。
    ($ 0行う呼び出し)前回のリードは__END__トークンでコンパイルを終了しているため、実際のPerlコードは一度だけコンパイルされます。 それは、単一引用符で囲まれた文字列のq +ガベージ+の一部であるため、実際のcompilaionは__END__トークンをバイパスします。
    コンパイルは#を無視する、内側行う発生するため、エラー行番号が正しく報告されています!。 )コンパイル時、マニュアルなどの実行時 ​​にエラーが、(死ぬことを呼び出し、両方のは、$ @文の場合、ダイの$ @によって早期に発見し、報告されています。 実際のPerlコードを一度にコンパイルされるので、各エラーは一度だけ報告されます。
    実際のコードは、任意の出口の数()、execを()、 フォーク()含まれており、(死ぬ)を呼び出し、期待どおりに動作します。サブルーチンの外に返すことがあります 。幸いなことに、純粋なPerlで許可されていませんので、我々は持っていませんこのケースを治療することができます。
    INC @プッシュは、「。」 のperl 5.8.0 -Tによって必要とされます。 

だから、実際のP​​erlコードは、ユーザーがプログラムを起動するどんなに、あっても古いUNIXシステム上で、実行されます。 ヘッダは、CGIスクリプトに含めるのに適しています。 (nonCGIプログラムでは、極端なセキュリティが重要でない場合、-Tオプションの出現を除去することができます。)

ロケール警告なしに完全に次の仕事のすべて、:

  DIR / nice.pl#好適灰DIR / nice.plののsh DIR / nice.pl bashのDIR / nice.pl cshのDIR / nice.pl tcshのDIR / nice.pl kshのDIR / nice.pl zshのDIR / nice.pl 
 DIR/nice.pl         # preferred
 ash  DIR/nice.pl
 sh   DIR/nice.pl
 bash DIR/nice.pl
 csh  DIR/nice.pl
 tcsh DIR/nice.pl
 ksh  DIR/nice.pl
 zsh  DIR/nice.pl

次の呼び出しは大丈夫です。

  perlの-x -S DIR / nice.pl#ロケール警告perlのDIR / nice.pl#ロケール警告perlはDIR / nice.pl#ロケール警告のperl -x -S nice.pl#ロケールに警告を-x; のみの#$ PATH上の場合、Win32のperlのnice.pl#ロケール警告にお勧めします。  CURDIRのperl -x nice.pl#ロケール警告からのみ。  (PATHが含まれているか、$ '。')CURDIR nice.pl#のみの$ PATH上の場合からのみ 
 perl -x -S DIR/nice.pl	# locale-warning
 perl DIR/nice.pl	# locale-warning
 perl -x DIR/nice.pl	# locale-warning
 perl -x -S nice.pl	# locale-warning; only if on # $PATH, recommended on Win32
 perl nice.pl		# locale-warning; only from curdir
 perl -x nice.pl	# locale-warning; only from curdir
 nice.pl		# only if on $PATH (or $PATH contains '.')


バギーのPerl 5.004しようとは/ binに実行するので、動作しない、次の/ shの-S nice.pl:

  perlの-S nice.pl#perlの-S DIR動作しない/ nice.pl#は動作しません。 
 perl -S nice.pl	# doesn't work 
 perl -S DIR/nice.pl	# doesn't work

もちろん、顕著なパフォーマンスペナルティがあります:/ binに/ shがスクリプトが呼び出されるたびに開始されます。 PERL_BADLANGは perlのが呼び出される前に設定する必要がありますので、これは完全に回避することはできません。 シェルの実行が終了した後、ヘルパーPerlコードの1行が(#!perlの後に)解析され、そしてDOは、ヘルパーコードの5行を解析する原因となります。 これらの6行に費やされる時間とメモリはごくわずかです。 だから、スクリプトの起動を遅くする唯一のアクションは、シェルです。 ユーザーセットと輸出は= xをPERL_BADLANG場合は、高速起動が呼び出すことによって可能です。

  perlの-x -S nice.plのperl -x DIR / nice.pl 
 perl -x -S nice.pl
 perl -x DIR/nice.pl

Makefileで、次のように記述する必要があります。

 輸出PERL_BADLANG = Xゴールます。perl -x DIR / nice.pl 
 export PERL_BADLANG=x
 goal:
    perl -x DIR/nice.pl

コマンドラインオプションは-nと- pは 、このヘッダに失敗します。 -nは しばらく の 間(<>){...}内のコードをラップとして実現することができるので、これは深刻な問題ではない、と-pは (<>){...} {印刷を継続しながら、ラッピングに変更することができます}。
ヘッダーウィザード

***ヘッダーウィザード [#o34aa9d9]

私は自動的に既存のPerlスクリプトにマジックPerlのヘッダーを追加しますヘッダーウィザードを実装しました。 ヘッダーウィザードはhttp://www.inf.bme.hu/~pts/Magic.Perl.Header/ magicc.pl.zipから入手可能です。 作者のサイトからダウンロードすると、あなたは最新バージョンを入手することを保証しても [ 便宜上、我々はまた、http://www.tpj.com/source/でこれを投稿しています。 -ed。]

普遍的に実行可能なPerlスクリプトのための簡単​​レシピ:

1.いつものようにあなたのPerlスクリプトを記述します。 あなたが好きなよう()exit()を呼び出して、 死ぬことがあります。
+いつものようにあなたのPerlスクリプトを記述します。 あなたが好きなよう()exit()を呼び出して、 死ぬことがあります。
+#を指定します! ...いつものようにperlのライン。 あなたは、任意の数のオプションが、-Tオプション欠落しているか、単独で指定する必要があります(スペースで区切っ)を置いてもよいです。 例:#! /ダミー/ perlの-wi.bak -T。 -Tオプションの詳細についてはperlrun(1)とperlsecを(1)を参照してください。
+スクリプトに右のオプションを含む8行マジックヘッダを付加し、それは(...はchmod + xは)スクリプト・ファイルを実行可能にします。3.ファイル名を指定して実行magicc.pl(ヘッダウィザード)、。 (Perlはそこだけスイッチを探しますので、-Tオプションは、execのperlの秒、およびその他のオプションの両方の後に移動されます。#!perlの後に移動されます。)
+が、コマンドライン上のshまたはperlのではないが、スラッシュでスクリプトを実行します。 たとえば、次のように./my_script.plのarg0 arg1にarg2に 。 あなたが$ PATHにスクリプトを移動した後、ちょうどmy_script.pl arg0の引数1 arg2のようにそれを実行します。 (これは、ロケールの警告を回避し、オプションを有効になります。)これらの呼び出しが何らかの理由でUNIXシステムに障害が発生した場合、私に電子メールにお気軽にお問い合わせください。 クイックフィックスのように、perlの-x -S ./my_script.plのarg0 arg1にarg2にしてスクリプトを実行します。
+Win32システム上で、perlの-x -Sは、スクリプトを実行するための唯一の方法である5.注意してください。 あなたはこれを行い個別の.batファイルを書き込むことができます。
+彼らはそれがあってもドキュメントに従わない人のために働くだろうが高い可能性がありますスクリプトをステップ4で説明した方法を実行する必要があることをユーザーに知らせます。

2.#を指定します! ...いつものようにperlのライン。 あなたは、任意の数のオプションが、-Tオプション欠落しているか、単独で指定する必要があります(スペースで区切っ)を置いてもよいです。 例:#! /ダミー/ perlの-wi.bak -T。 -Tオプションの詳細についてはperlrun(1)とperlsecを(1)を参照してください。
***結論 [#u73c604d]

スクリプトに右のオプションを含む8行マジックヘッダを付加し、それは(...はchmod + xは)スクリプト・ファイルを実行可能にします。3.ファイル名を指定して実行magicc.pl(ヘッダウィザード)、。 (Perlはそこだけスイッチを探しますので、-Tオプションは、execのperlの秒、およびその他のオプションの両方の後に移動されます。#!perlの後に移動されます。)

4.が、コマンドライン上のshまたはperlのではないが、スラッシュでスクリプトを実行します。 たとえば、次のように./my_script.plのarg0 arg1にarg2に 。 あなたが$ PATHにスクリプトを移動した後、ちょうどmy_script.pl arg0の引数1 arg2のようにそれを実行します。 (これは、ロケールの警告を回避し、オプションを有効になります。)これらの呼び出しが何らかの理由でUNIXシステムに障害が発生した場合、私に電子メールにお気軽にお問い合わせください。 クイックフィックスのように、perlの-x -S ./my_script.plのarg0 arg1にarg2にしてスクリプトを実行します。

Win32システム上で、perlの-x -Sは、スクリプトを実行するための唯一の方法である5.注意してください。 あなたはこれを行い個別の.batファイルを書き込むことができます。

6.彼らはそれがあってもドキュメントに従わない人のために働くだろうが高い可能性がありますスクリプトをステップ4で説明した方法を実行する必要があることをユーザーに知らせます。
結論

このような、広く実装された言語のために、Perlは様々なプラットフォーム上で確実に呼び出すことが驚くほど難しいことができます。 私はこのヘッダーウィザードがちょうど約あらゆるシステム上で最小限の手間で開始されますPerlスクリプトを書くために役立ちます願っています。

TPJ

*Example 1: The Magic Perl Header [#j169cda4]
 #! /bin/sh
 eval '(exit $?0)' && eval 'PERL_BADLANG=x;PATH="$PATH:.";export PERL_BADLANG\
 ;exec perl -T -x -S -- "$0" ${1+"$@"};#'if 0;eval 'setenv PERL_BADLANG x\
 ;setenv PATH "$PATH":.;exec perl -T -x -S -- "$0" $argv:q;#'.q
 #!perl -w
 +push@INC,'.';$0=~/(.*)/s;do(index($1,"/")<0?"./$1":$1);die$@if$@__END__+if 0
 ;#Don't touch/remove lines 1--7: http://www.inf.bme.hu/~pts/Magic.Perl.Header
 # real Perl code begins here