このスクリプトを実行すると、終了するまで実行されます。
# 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
にすぎません。しかし、#!
" "は単なるコメントよりも重要な意味を持っていますか?
答え1
このスクリプトは現在インタラクティブシェルを持ち、-lineなしでスクリプトを実行すると実行されbash
ます。プロセスは出力にと表示されます。#!
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
。
ファイルが実行可能な形式ではないか、ディレクトリでないために実行に失敗した場合、シェルスクリプトだとすると、シェルコマンドを含むファイル。 サブシェルの作成それを実行するために。サブシェルは自分自身を再初期化して、次のことを行います。その効果は、スクリプトを処理するために新しいシェルが呼び出されるのと同じです。しかし、親が覚えているコマンドの場所(以下の「SHELL BUILTIN COMMANDS」の下のハッシュを参照)は子が保持します。
プログラムがで始まるファイルの場合、
#!
最初の行の残りの部分はプログラムのインタプリタを指定します。 シェルは指定されたインタプリタを実行します。既定では、この実行可能ファイルの種類を処理しないオペレーティング システムで。 [...]
つまり、-lineを使用せずにコマンド./foo.sh
ラインからコマンドを実行することは、サブシェルのファイルでコマンドを実行するのと同じです。foo.sh
#!
$ ( echo "$BASHPID"; while true; do sleep 1; done )
そして#!
たとえば、次を指す正しい行は次/bin/bash
のとおりです。
$ /bin/bash foo.sh
答え2
シェルスクリプトがで始まる場合、#!
最初の行はシェルのコメントです。ただし、最初の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
対照的に、Dashはパラメータとして渡されたスクリプトパスを使用してそれを呼び出してENOEXEC
応答します。/bin/sh
つまり、ダッシュでshebanglessスクリプトを実行すると、スクリプトに#!/bin/sh
.Mkshとzshの動作があるかのように動作します。
% dash
$ ./foo.sh
PID TTY STAT TIME COMMAND
21427 pts/2 S+ 0:00 /bin/sh ./foo.sh
答え3
最初のケースでは、スクリプトは現在のシェルから分岐した子プロセスによって実行されます。
まず、echo $$
シェルのプロセスIDを親プロセスIDとして使用してシェルを実行してから確認する必要があります。