Perlのマニュアルでは、次のように、csh、sh、またはPerlのいずれかで機能する完全に不正な構造について説明しています。
eval '(exit $?0)' && eval 'exec perl -wS $0 ${1+"$@"}'
& eval 'exec /usr/bin/perl -wS $0 $argv:q'
if $running_under_some_shell;
確かによこしまな...誰かがこれがどのように機能するかを詳細に説明できますか?
これらの3行は、標準のBourneシェル(sh)、Cシェル(csh)、またはPerlで評価された場合、3つの異なることを行うという考え方です。#!
このハックは、スクリプトの先頭で行を使用してインタプリタ名を指定することをサポートしていないシステムでのみ必要です。これらの3行で始まるPerlスクリプトをシェルスクリプトとして実行すると、シェルはPerlインタープリターを起動し、スクリプトのファイル名とコマンドライン引数を渡します。
Perlでは、3行は、で終わる1つのステートメントを;
形成します。
eval '...' && eval '...' & eval '...' if $running_under_some_shell;
スクリプトが開始されたばかりなので、はfalseで$running_under_some_shell
ありundef
、これはfalseであり、評価は実行されません。それはノーオペレーションです。
不正な部分は$?0
、shとcshで異なる方法で解析されることです。shでは、これは$?
(最後のコマンドの終了ステータス)の後に0が続くことを意味します。前のコマンドがない$?
ため、0になり、に$?0
評価され00
ます。cshでは、$?0
は特別な変数であり、現在の入力ファイル名がわかっている場合は1、不明な場合は0です。シェルはスクリプトからこれらの行を読み取っているので、は$?0
1になります。
したがって、shでは、eval '(exit $?0)'
を意味eval '(exit 00)'
し、cshでは。を意味しeval '(exit 1)'
ます。パレンスは、exitコマンドをサブシェルで評価する必要があることを示しています。
shとcshはどちらも&&
、「前のコマンドを実行し、前のコマンドが0を終了した場合にのみ次のコマンドを実行する」という意味を理解しています。したがって、shのみが実行されeval 'exec perl -wS $0 ${1+"$@"}'
ます。cshは次の行に進みます。
cshは、行の先頭にある「&」を無視します。(それがcshにとって何を意味するのか正確にはわかりません。その目的は、Perlの観点からこれを単一の式にすることです。)次に、cshはの評価に進みeval 'exec /usr/bin/perl -wS $0 $argv:q'
ます。
これらの2つのコマンドラインは非常に似ています。 exec perl
のコピーを起動して現在のプロセスを置き換えることを意味しますperl
。 (警告を有効にする)および(で指定されたスクリプトを探す)と-wS
同じ意味です。 スクリプトのファイル名です。最後に両方とも、現在のコマンドライン引数のコピーを生成します(それぞれshとcshで)。-w
-S
$PATH
$0
${1+"$@"}
$argv:q
これは${1+"$@"}
、通常の代わりに"$@"
、Bourneシェルのいくつかの古いバージョンのバグを回避するために使用します。それらは同じことを意味します。詳細はBennettToddの説明(gbaconの回答にコピーされています)で読むことができます。
トム・クリスチャンセンのコレクションより、あなたが知りたいと思っていたすべてよりもはるかに…</a>:
eval 'exec perl $0 -S ${1+"$@"}'
ニュースグループ: comp.lang.tcl,comp.unix.shell
From: bet@ritz.mordor.com (Bennett Todd)
件名: Re:"$@"
vsフォロー${1+"$@"}
アップ宛先: comp.unix.shell
Date: Tue, 26 Sep 1995 14:35 :45 GMT
メッセージ ID: <DFIoJL.934@ritz.mordor.com>(これは実際には TCL の質問ではありません。Bourne Shell の質問です。そのため、comp.unix.shell に相互投稿し、フォローアップを設定しました)。
むかしむかし (またはその通り)、コマンドライン全体を補間するための 2 つの選択肢を提供する Bourne Shell がどこかにありました。最も単純なものは
$*
で、すべての引数を borfed しただけで、内部の空白を保護していた引用符が失われました。"$@"
また、空白を保護するために も提供しました。これで、icko ビットがどのよう"$@"
に実装されました。この初期のシェルでは、2 文字のシーケンス$@
は次のように補間されます。$1" "$2" "$3" "$4" ... $n
周囲の引用符を追加すると、シュメール全体の引用が終了しました。かわいい、かわいい、かわいすぎる…。では正しい使い方を考えてみましょう。
"$@"
args がない場合は次のように展開されます:
""
それが空の文字列です — 長さゼロの単一の引数です。これは、引数がまったくない ことと同じではありません。そこで、誰かが Bourne Shell のもう 1 つの機能である条件付き補間の巧妙なアプリケーションを考え出しました。イディオム
${varname+value}
value
が設定されている場合に展開され、それ以外の場合varname
は何も展開されません。したがって、議論中のイディオム${1+"$@"}
正確に、単純とまったく同じを意味します
"$@"
その古代の非常に奇妙なバグがなければ。
では、問題は、どの砲弾にそのバグがあったかということです。それを含む漠然と最近のOSに同梱されているシェルはありますか?
-- -ベネット bet@mordor.com http://www.mordor.com/bet/