この質問のバリエーションが何度も要求されました(ここそしてここ、例えば)しかし、答えが私の質問を完全に捉えていないか、私が知っているよりも多くを想定するのは心配です。
たとえば、質問をしますが、おおよそ私が理解しようとしているのは(1)どのようにシェルは実行可能ファイルとスクリプトを認識できますが、もしそうなら(2)認識後に次に何が起こるのか?
私の作業ディレクトリにシェルスクリプトと実行可能ファイルがあるとしますscript
(「実行可能」はバイナリ「マシンコード」を意味すると理解していますが、正確ではないかもしれません)exe
。 bashシェルと対話していて、script
bashとtcshの両方がこれを実行できるとしましょう。さらに、最初の行を仮定すると確かにShebangで始めましょう#!...
。
script
コマンドラインプロンプトに入力するとします。 Bashはこれが実行可能ファイルではなくスクリプトであるかどうかを判断し、判断したら何をしますか? (私の考えに答えると、「新しいプロセス、特に新しいシェル(サブシェルなど)を作成し、この場合bash(shebangがないため)を作成し、その中にあるスクリプトからコマンドを実行することです」と思います。わからない。)exe
それでは、コマンドラインプロンプトに入力したとします。 Bashはこれがスクリプトではなく実行可能ファイルであるかどうかを判断しますか?一度決定したら何をしますか? (私の考えでは、「新しいプロセスを作成して実行可能ファイルが完了するのを待つ」という答えが出てくるようですが、わかりません。)それでは、最初の行に
script
含めるように変更したとしましょう。#! /bin/tcsh
bashはこれがスクリプト((1)への答えでshebangが何を変更しますか?)であり、実行可能ファイルではないことをどのように決定し、決定したら何をしますか? (私の考えに答えると、「新しいプロセス、特に新しいシェル(つまりサブシェル)を作成し、この場合はtcsh(私はshebangだから)を作成し、その中のスクリプトでコマンドを実行することです」と思います。私はそうではありません。
ベストアンサー1
まず、コマンドを実行するシステムがあります。
このシェルコードを使用すると:
cmd and its args
シェルは、リストされているディレクトリー内のcmd
別名、関数、組み込み、および実行可能ファイル(実行権限を持つ通常のファイル)を探します$PATH
(現在の作業ディレクトリーがそのディレクトリーにない限り、$PATH
これは悪い習慣です)。 。
後者の場合、execve()
通常は3つの引数を使用して子プロセスからシステムコールを呼び出します。
- ファイルパス(
/path/to/cmd
) - パラメータリスト(
["cmd", "and", "its", "args", 0]
) var=value
エクスポートされた各変数の文字列のリスト。
execve()
ファイルの種類に応じてファイルを処理する場合:
- ELFバイナリ実行可能ファイルの場合は、そのファイル(またはその一部)をメモリにロード/マッピングし、より多くの共有ライブラリをロードして実行を開始する動的リンカーも可能です。
- で始まる場合
#!
(シェルではない)、行の残りの部分を実行する別のファイルとして解釈し、ファイルパスをコマンドの追加引数として渡します。 (もしそうなら#! foo bar
、同じことをしますexecve("foo", ["foo" or "cmd", "bar", "/path/to/cmd", "and", "its", "args"], env)
)。 - システムはELFに加えてさまざまなデフォルトの実行可能ファイル形式をサポートでき、一部のシステムはインタプリタをファイルの先頭に一致するパターンに関連付けるように設定できます(Linuxのbinfmt_miscを参照)。
プロセスメモリは、プロセス中にほとんど消去され、プロセスが実行可能ファイルexecve()
でコードを実行しているため、決して返されません。
ファイルの種類が認識されない場合は、エラーコードに戻り(失敗表示)execve()
されます。-1
ENOEXEC
この場合、POSIXシェルが必要であり、これをスクリプトとして処理するには、execlp()
C関数(システムコールではない)やenv
/ ...(または通常はPOSIXツールボックスでコマンドを実行するために使用されるすべての項目)find -exec
などが必要sh
です。ランダムに実行されるのを避けるために経験的な方法を使用しているようですsh
。
sh
ほとんどのシェルは shebang があるかのように実行してこれを行います#! /path/to/the/standard/sh -
。一部はPOSIX準拠のsh
実装であるか、サブプロセスでファイル自体を解釈してこれを実行するPOSIX shモードを使用します。
したがって、3つのシナリオの場合:
- shebang-lessの代わりに
script
実行すると機能します。シェルは子プロセスで実行され、既知の形式ではないため、ENOEXECで失敗するため、シェルは(オプションで)どのように見えるかを確認します。内で実行できます。構文(またはバリアント)またはそれ自体で解釈されます(親シェルプロセスは終了するのを待ちます)。./script
script
/usr/bin/script
execve("./script", ["./script", 0], env)
execve()
sh
execve("/bin/sh", ["sh", "-", "./script"])
argv[0]
-
./exe
:shellはexecve("./exe", ["./exe", 0], env)
子プロセスでこれを行い、子プロセスは成功し、親プロセスはそのタスクが終了するのを待ちます。./script
#! /bin/tcsh
shebang:shellを使用すると、execve("./script", ["./script", 0], env)
システムはそれをスクリプトとして認識するため、成功することもできます。パラメータを使用して順番に実行されexecve()
ます。親シェルは通常どおりサブシェルを待ちます。/bin/tcsh
./script