web-dev-qa-db-ja.com

「ps ax」が「#!」なしで実行中のbashスクリプトを見つけられないのはなぜですかヘッダ?

このスクリプトを実行すると、殺されるまで実行するように意図されていました...

# 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」自体は​​コメントにすぎません。しかし、「#!」は単なるコメントよりも重要な意味を持っていますか?

13
StoneThrow

現在のインタラクティブシェルが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.shfoo.sh行がない場合、コマンドラインで#!を実行することは、サブシェルでファイル内のコマンドを実行することと同じです。

$ ( echo "$BASHPID"; while true; do sleep 1; done )

Withを指す適切な#!行。 /bin/bash、そうです

$ /bin/bash foo.sh
13
Kusalananda

シェルスクリプトが#!で始まる場合、シェルに関する限り、最初の行はコメントです。ただし、最初の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