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

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

答え1

このスクリプトは現在インタラクティブシェルを持ち、-lineなしでスクリプトを実行すると実行されbashます。プロセスは出力にと表示されます。#!bashps axbash

$ 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として使用してシェルを実行してから確認する必要があります。

関連情報