なぜなら彼らの一部がそうしているからです。
> echo echo Hallo, Baby! | iconv -f utf-8 -t utf-16le > /tmp/hallo
> chmod 755 /tmp/hallo
> dash /tmp/hallo
Hallo, Baby!
> bash /tmp/hallo
/tmp/hallo: /tmp/hallo: cannot execute binary file
> (echo '#'; echo echo Hallo, Baby! | iconv -f utf-8 -t utf-16le) > /tmp/hallo
> bash /tmp/hallo
Hallo, Baby!
> mksh /tmp/hallo
Hallo, Baby!
> cat -v /tmp/hallo
#
e^@c^@h^@o^@ ^@H^@a^@l^@l^@o^@,^@ ^@B^@a^@b^@y^@!^@
^@
これは実際に互換性の問題ですか?必須標準的に?かなり危険で予期せぬように見えるからです。
答え1
~によるとPOSIX、
入力ファイルはテキストファイルでなければなりませんが、行の長さは制限されません。^
入力のNUL文字²テキストではないものにする、POSIXに関する限り動作は指定されていないので、sh
実装は必要に応じて何でも行うことができます(そしてPOSIXと互換性があります)。スクリプトNULを含めることはできません)。
一部のシェルは、最初の数バイトからゼロを検索し、誤ってスクリプトではなくファイルを実行しようとすると、スクリプトの実行を拒否します。
exec*p()
これは関数、env
コマンドsh
、find -exec
...のために便利です。必須システムがENOEXECを返すと、コマンドを解釈するためにシェルが呼び出されますexecve()
。したがって、間違ったアーキテクチャに対してコマンドを実行したい場合は、次のようにするのが最善です。バイナリは実行されません。シェルで発生するファイルエラーは、それをシェルスクリプトとして理解しようとするシェルの試みで発生したエラーではありません。
これはPOSIXで許可されています。
実行可能ファイルがテキストファイルではない場合、シェルはこのコマンドの実行をバイパスできます。
本規格の次の改訂版ではに変更されます:
シェルは、実行されるファイルがスクリプトであるかどうかを判断するために経験的チェックを適用することができ、ファイルがスクリプトでないと判断した場合は、このコマンドの実行をバイパスできます。この場合、エラーメッセージを作成して終了ステータス126を返す必要があります。
注:スクリプトにできないファイルを拒否する一般的な経験的な方法は、固定長内の<newline>バイトの前にNULバイトファイルプレフィックスを置くことです。 shは行の長さが無制限の入力ファイルを許可する必要があるため、経験的チェックは行の長さに基づいて行うことはできません。
この動作により、アーカイブにシェルヘッダーとバイナリデータが含まれていても、シェルの自動抽出アーカイブを防ぐことができます。
シェルはzsh
入力でNULをサポートしますが、NULはの引数として渡すことはできないため、その引数または名前execve()
にのみ使用できます。組み込みコマンドまたは機能:
$ printf '\0() echo zero; \0\necho \0\n' | zsh | hd
00000000 7a 65 72 6f 0a 00 0a |zero...|
00000007
(ここでは、NULを名前として関数を定義して呼び出し、NUL文字を組み込みコマンドecho
の引数として渡します。)
皮をむく人もいるが、それも賢明なことだ。NUL
sは時々フィラーとして使用されます。たとえば、端末では無視されます (キャリッジ リターン (文字通り) などの複雑な制御シーケンスを処理する時間を持つために端末に送信される場合もあります)。ファイルの穴はNULなどで埋められているようです。
非テキストは NUL バイトに限定されません。また、ロケールで有効な文字を形成しないバイト列でもあります。たとえば、0xc1バイト値はUTF-8でエンコードされたテキストには表示できません。したがって、文字エンコーディングとしてUTF-8を使用するロケールでは、これらのバイトを含むファイルは有効なテキストファイルではなく、有効なsh
スクリプトでもありません。
実際に、yash
これは私がこのような誤った入力について文句を言う唯一のシェルです。
1 この規格の次の改訂版ではそれは変わるでしょう到着
入力ファイルは任意のタイプにすることができますが、シェル構文(XREF - XSH 2.10.2シェル構文規則)に基づいて解析されるファイルの初期部分は文字で構成する必要があり、NUL文字を含めないでください。シェルは行長制限を適用しないでください。
自己抽出アーカイブを説明するために残りの部分にNULが含まれていても、NULバイトなしで有効な構文部分で始まる入力をサポートするには、シェルが明示的に必要です。
²と文字は、ロケールの文字エンコーディング(出力を参照locale charmap
)に従ってデコードされるようになっており、POSIXシステムでは、NUL文字(エンコーディングは常にバイト0)は、エンコードにバイト0を含む唯一の文字です。つまり、UTF-16はPOSIXロケールで使用できる文字エンコーディングではありません。
LANG
3 ただし、スクリプト内で(たとえば// /変数を割り当てると)ロケールの変更に問題があり、LC_CTYPE
変更が入力を解釈するシェルに適用されることがあります。LC_ALL
LOCPATH
答え2
この動作の理由は少し複雑です。
まず、最新のシェルにはヌルバイトを含む潜在的なバイナリファイルのチェックが含まれていますが、このチェックはファイルの最初の行のみをチェックします。これが最初の行の「#」が動作を変更する理由です。歴史的なBourne Shellにはバイナリチェックがなく、前述の方法で実行するために「#」も必要ありませんでした。
Bourne Shellで使用される特定の方法は、mbtowc()
すべてのヌルバイトをスキップする方法でマルチバイト文字をサポートします。mbtowc()
ヌルバイトの場合、文字長0が返され、ループが次の文字を再試行するようになるためです。
Bourne Shellは1988年頃にこのコードを導入し、他のシェルもこの動作をコピーした可能性があります。