このスクリプトを実行すると、殺されるまで実行するように意図されていました...
# foo.sh
while true; do sleep 1; done
...ps ax
を使用してそれを見つけることができません:
>./foo.sh
// In a separate Shell:
>ps ax | grep foo.sh
21110 pts/3 S+ 0:00 grep --color=auto foo.sh
...しかし、一般的な "#!
"ヘッダーをスクリプトに追加するだけなら...
#! /usr/bin/bash
# foo.sh
while true; do sleep 1; done
...その後、同じps
コマンドでスクリプトが検索可能になります...
>./foo.sh
// In a separate Shell:
>ps ax | grep foo.sh
21319 pts/43 S+ 0:00 /usr/bin/bash ./foo.sh
21324 pts/3 S+ 0:00 grep --color=auto foo.sh
これはなぜですか?
これは関連する質問である可能性があります。「#
」は単なるコメントプレフィックスであり、そうであれば「#! /usr/bin/bash
」自体はコメントにすぎません。しかし、「#!
」は単なるコメントよりも重要な意味を持っていますか?
現在のインタラクティブシェルがbash
であり、#!
- lineなしでスクリプトを実行すると、bash
がスクリプトを実行します。プロセスはps ax
出力にbash
として表示されます。
$ cat foo.sh
# foo.sh
echo "$BASHPID"
while true; do sleep 1; done
$ ./foo.sh
55411
別のターミナルで:
$ ps -p 55411
PID TT STAT TIME COMMAND
55411 p2 SN+ 0:00.07 bash
関連:
関連セクションはbash
マニュアルを形成します:
ファイルが実行可能形式ではなく、ファイルがディレクトリではないためにこの実行が失敗した場合シェルスクリプト、ファイルであると見なされますシェルコマンドを含みます。 それを実行するためにサブシェルが生成されます。このサブシェルは自身を再初期化するので、スクリプトの処理のために新しいシェルが呼び出されたかのようになります。ただし、コマンドの場所が記憶されている点が異なります。親によって(シェルビルトコマンドの下のハッシュを参照)、子によって保持されます。
プログラムが
#!
で始まるファイルの場合、最初の行の残りの部分はプログラムのインタープリターを指定します。 シェルは、この実行可能形式自体を処理しないオペレーティングシステムで、指定されたインタープリターを実行します。 [...]
つまり、./foo.sh
にfoo.sh
行がない場合、コマンドラインで#!
を実行することは、サブシェルでファイル内のコマンドを実行することと同じです。
$ ( echo "$BASHPID"; while true; do sleep 1; done )
Withを指す適切な#!
行。 /bin/bash
、そうです
$ /bin/bash foo.sh
シェルスクリプトが#!
で始まる場合、シェルに関する限り、最初の行はコメントです。ただし、最初の2文字はシステムの別の部分、つまりカーネルにとって意味があります。 2つの文字#!
は Shebang と呼ばれます。シバンの役割を理解するには、プログラムの実行方法を理解する必要があります。
ファイルからプログラムを実行するには、カーネルからのアクションが必要です。これは execve
システムコールの一部として行われます。カーネルは、ファイルのアクセス許可を確認し、現在呼び出しプロセスで実行されている実行可能ファイルに関連付けられているリソース(メモリなど)を解放し、新しい実行可能ファイルにリソースを割り当て、新しいプログラムに制御を転送する必要があります(およびその他のこと)私は言及しません)。 execve
システムコールは、現在実行中のプロセスのコードを置き換えます。別のシステムコール fork
があり、新しいプロセスを作成します。
これを行うには、カーネルが実行可能ファイルの形式をサポートしている必要があります。このファイルには、カーネルが理解できる方法で編成されたマシンコードが含まれている必要があります。シェルスクリプトにはマシンコードが含まれていないため、この方法で実行することはできません。
Shebangメカニズムにより、カーネルはコードを解釈するタスクを別のプログラムに延期できます。カーネルは、実行可能ファイルが#!
で始まることを検出すると、次の数文字を読み取り、ファイルの最初の行(先頭の#!
およびオプションのスペースを差し引いたもの)を別のファイルへのパス(プラス引数。ここでは説明しません)。カーネルがファイル/my/script
を実行するように指示され、ファイルが行#!/some/interpreter
で始まることがわかると、カーネルは/some/interpreter
を引数/my/script
で実行します。次に、/some/interpreter
が実行するスクリプトファイルであると判断するのは/my/script
までです。
カーネルが理解できる形式のネイティブコードがファイルに含まれておらず、シバンで始まっていない場合はどうなりますか?さて、そのファイルは実行可能ではなく、execve
システムコールはエラーコードENOEXEC
(実行可能フォーマットエラー)で失敗します。
これで話は終わりですが、ほとんどのシェルはフォールバック機能を実装しています。カーネルがENOEXEC
を返す場合、シェルはファイルの内容を調べ、それがシェルスクリプトのように見えるかどうかを確認します。シェルは、ファイルがシェルスクリプトのように見えると判断した場合、それ自体を実行します。これを行う方法の詳細は、シェルによって異なります。スクリプトにps $$
を追加すると、何が起こっているかを確認できます。さらに、strace -p1234 -f -eprocess
でプロセスを監視すると、1234がシェルのPIDになります。
Bashでは、このフォールバックメカニズムはfork
ではなくexecve
を呼び出すことで実装されます。子bashプロセスはそれ自体で内部状態をクリアし、新しいスクリプトファイルを開いて実行します。したがって、スクリプトを実行するプロセスは、元のbashコードイメージと、bashを最初に呼び出したときに渡された元のコマンドライン引数をまだ使用しています。 ATT kshも同じように動作します。
% bash --norc
bash-4.3$ ./foo.sh
PID TTY STAT TIME COMMAND
21913 pts/2 S+ 0:00 bash --norc
対照的に、ダッシュはENOEXEC
に反応し、引数として渡されたスクリプトへのパスを使用して/bin/sh
を呼び出します。つまり、ダッシュからシバンレススクリプトを実行すると、スクリプトには#!/bin/sh
のシバン行があるかのように動作します。 Mkshとzshは同じように動作します。
% dash
$ ./foo.sh
PID TTY STAT TIME COMMAND
21427 pts/2 S+ 0:00 /bin/sh ./foo.sh