= コマンドの実行とその環境
:encoding: UTF-8
:lang: ja
//:title: Yash マニュアル - コマンドの実行とその環境
:description: このページではコマンドがどのように実行されるかを説明します。

この節ではコマンドがどのように実行されるかを説明します。

[[simple]]
== 単純コマンドの実行

link:syntax.html#simple[単純コマンド]は以下の手順に従って実行されます。

. 単純コマンドに含まれる、変数代入とリダイレクト以外のトークンを全て{zwsp}link:expand.html[展開]します。展開エラーが発生した場合は、この単純コマンドの実行は中止されます (このとき単純コマンドの終了ステータスは非 0 です)。
  +
  以下、展開の結果得られた最初の単語をdfn:[コマンド名]、それ以外の単語をdfn:[コマンド引数]と呼びます。得られた単語が一つの場合は、コマンド引数は存在しません。得られた単語が一つもない場合は、コマンド名もコマンド引数も存在しません。
. 単純コマンドに対する{zwsp}link:redir.html[リダイレクト]を実行します。リダイレクトに含まれるトークンの展開はここで行われます。リダイレクトエラーが発生した場合は、この単純コマンドの実行は中止されます (このとき単純コマンドの終了ステータスは非 0 です)。リダイレクトに含まれるトークンの展開時のエラーはリダイレクトエラーに含まれます。
. 単純コマンドに含まれる変数代入を実行します (配列の代入を含む)。それぞれの変数代入について、値が展開され、指定された名前の{zwsp}link:params.html#variables[変数]に代入されます。代入エラーが発生した場合は、この単純コマンドの実行は中止されます (このとき単純コマンドの終了ステータスは非 0 です)。代入される値の展開時のエラーは代入エラーに含まれます。
+
--
- コマンド名が存在しないか、あるいはコマンド名が{zwsp}link:builtin.html#types[特殊組込み]を示している場合は、変数代入は恒久的です。すなわち、代入の結果はこの単純コマンドの実行が終わった後もそのまま残ります。
- それ以外の場合は、変数代入は一時的です。すなわち、代入の効果はこの単純コマンドの実行中のみ有効で、実行が終わった後に代入は取り消されます。
--
+
コマンド名が指定された場合または link:_set.html#so-allexport[all-export オプション]が有効な場合は、代入される変数は自動的に{zwsp}link:params.html#variables[エクスポート]対象になります。
+
[NOTE]
Yash 以外のシェルでは代入の動作が異なることがあります。特殊組込みまたは関数では変数はエクスポート対象にならないかもしれません。また関数の実行終了後も変数が残るかもしれません。

. コマンド名が存在しない場合は、単純コマンドの実行はこれで終わりです。単純コマンドの終了ステータスは 0 になります (ただし単純コマンド実行中に{zwsp}link:expand.html#cmdsub[コマンド置換]が行われた時は、最後のコマンド置換のコマンドの終了ステータスが単純コマンドの終了ステータスになります)。
. 後述の<<search,コマンドの検索>>の仕方に従って実行すべきコマンドを特定し、そのコマンドを実行します。
+
--
- コマンドが外部コマンドの場合は、コマンドは<<subshell,サブシェル>>で exec システムコールを呼び出すことにより実行されます。コマンド名とコマンド引数が起動するコマンドに渡されます。またエクスポート対象となっている変数が環境変数としてコマンドに渡されます。
- コマンドが{zwsp}link:builtin.html[組込みコマンド]の場合は、コマンド引数を引数として組込みコマンドが実行されます。
- コマンドが<<function,関数>>の場合は、その関数の内容が実行されます。コマンド引数が関数の引数として渡されます。

実行したコマンドの終了ステータスがこの単純コマンドの終了ステータスになります。コマンドが見つからなかった場合は、コマンドは実行されず終了ステータスは 127 になります。コマンドが見つかったが起動に失敗した場合は、終了ステータスは 126 になります。コマンドが起動されたがシグナルによって中断された場合は、終了ステータスはそのシグナルの番号に 384 を足した数になります。

[NOTE]
POSIX ではコマンドがシグナルによって中断された場合の終了ステータスは 128 より大きな数としか定められていないので、yash 以外のシェルでは終了ステータスが異なることがあります。

非 link:posix.html[POSIX 準拠モード]においてコマンドが見つからなかったとき、コマンド
ifdef::basebackend-html[]
pass:[<code><a href="_eval.html">eval</a> -i -- "${COMMAND_NOT_FOUND_HANDLER-}"</code>]
endif::basebackend-html[]
ifndef::basebackend-html[`eval -i -- "${COMMAND_NOT_FOUND_HANDLER-}"`]
が実行されます。ただしこのとき{zwsp}link:params.html#positional[位置パラメータ]はコマンド名とコマンド引数に一時的に置き換えられます。またこのコマンドの実行中に定義された<<localvar,ローカル変数>>はこのコマンドの終了時に削除されます。このコマンドの実行時には link:params.html#sv-handled[++HANDLED++ ローカル変数]が空文字列を値としてあらかじめ定義されます。このコマンドの実行後にこの変数の値が空文字列でなくなっていれば、このコマンドの終了ステータスがこの単純コマンドの終了ステータスとなり、コマンドが見つからなかったことはエラーとはみなされません。
--

[[search]]
=== コマンドの検索

単純コマンドで実行すべきコマンドは、展開で得られたコマンド名に基づいて以下の手順で特定されます。

. コマンド名にスラッシュ (+/+) が含まれている場合は、それが実行すべき外部コマンドへのパス名であると特定されます。
. コマンド名が{zwsp}link:builtin.html#types[特殊組込みコマンド]ならば、その組込みコマンドが実行すべきコマンドとして特定されます。
. コマンド名と同じ名前の<<function,関数>>が存在すれば、その関数が実行すべきコマンドとして特定されます。
. コマンド名が{zwsp}link:builtin.html#types[準特殊組込みコマンド]ならば、その組込みコマンドが実行すべきコマンドとして特定されます。
. コマンド名が{zwsp}link:builtin.html#types[通常の組込みコマンド]ならば、その組込みコマンドが実行すべきコマンドとして特定されます。(link:posix.html[POSIX 準拠モード]のときを除く)
. link:params.html#sv-path[+PATH+ 変数]の値に従って、実行すべき外部コマンドを検索しそのパス名を特定します。
+
--
+PATH+ 変数の値は、いくつかのディレクトリのパス名をコロン (+:+) で区切ったものとみなされます (空のパス名はシェルの作業ディレクトリを表しているものとみなします)。それらの各ディレクトリについて順に、ディレクトリの中にコマンド名と同じ名前の実行可能な通常のファイルがあるか調査します。そのようなファイルがあれば、そのファイルが実行すべき外部コマンドとして特定されます (ただし、コマンド名と同じ名前の組込みコマンドがあれば、代わりにその組込みコマンドが実行すべきコマンドとして特定されます)。どのディレクトリにもそのようなファイルが見つからなければ、実行すべきコマンドは見つからなかったものとみなされます。
--

外部コマンドの検索が成功しパス名が特定できた場合、そのパス名が絶対パスならば、シェルはそのパス名を記憶し、再び同じコマンドを実行する際に検索の手間を省きます。ただし、再びコマンドを実行しようとした際に、記憶しているパス名に実行可能ファイルが見当たらない場合は、検索をやり直します。シェルが記憶しているパス名は link:_hash.html[hash 組込みコマンド]で管理できます。

[[exit]]
== シェルの終了

シェルは、入力が終わりに達して全てのコマンドを解釈・実行し終えた時や、{zwsp}link:_exit.html[exit 組込みコマンド]を実行したときなどに終了します。シェルの終了ステータスは、シェルが最後に実行したコマンドの終了ステータスを 256 で割った余りです (一つもコマンドを実行しなかったときは 0)。

link:_trap.html[Trap 組込みコマンド]でシェル終了時のハンドラが登録されている場合は、シェルが終了する直前にそのハンドラが実行されます。ただしこのハンドラ内で実行したコマンドはシェルの終了ステータスには影響しません。

link:interact.html[対話モード]でないシェルの実行中に下記エラーが発生した場合、シェルは直ちに終了します。このときシェルの終了ステータスは非 0 です。

- 文法エラーのためコマンドを解釈できないとき (link:invoke.html#init[シェルの初期化]中を除く)
- link:posix.html[POSIX 準拠モード]で、{zwsp}link:builtin.html#types[特殊組込みコマンド]の実行中にエラーが発生したとき
- POSIX 準拠モードで、特殊組込みコマンドに対して{zwsp}link:redir.html[リダイレクト]エラーが発生したとき
- link:syntax.html#simple[単純コマンド]で代入エラーが発生したとき
- link:expand.html[展開]エラーが発生したとき (シェルの初期化中を除く)

[NOTE]
Yash はそうではありませんが、<<search,コマンドの検索>>において実行すべきコマンドが見つからなかったときに直ちに終了するようなシェルもあります。

[[function]]
== 関数

dfn:[関数]は一つの複合コマンドを単純コマンドのように呼び出せるようにする機構です。関数は{zwsp}link:syntax.html#funcdef[関数定義コマンド]によって定義でき、{zwsp}link:syntax.html#simple[単純コマンド]によって実行できます。関数を削除するには link:_unset.html[unset 組込みコマンド]を使います。

Yash には、シェルの起動時に最初から定義されている関数は一つもありません。

関数の実行は、関数の内容である複合コマンドを実行することによって行われます。関数の実行中は、関数の引数が{zwsp}link:params.html#positional[位置パラメータ]になります。それまでの位置パラメータは一時的に使えなくなりますが関数の実行が終わった時に元の位置パラメータに戻ります。

[[localvar]]
=== ローカル変数

dfn:[ローカル変数]とは、関数の実行中にだけ有効な一時的な変数です。ローカル変数は link:_typeset.html[typeset 組込みコマンド]を使って作ることができます。関数の実行中に作られたローカル変数は関数の実行が終わった時に削除され、ローカル変数を作る前の元の変数の状態に戻ります。

関数内で定義したローカル変数は、関数の実行に先立って定義してあった同名の他の変数を__隠蔽__します。隠蔽された変数は、関数の実行が終わってローカル変数がなくなるまで使えなくなります。

関数の実行中でないときにローカル変数を作ることはできません。ローカル変数を作ろうとしても、通常の変数になります。

[[environment]]
== コマンドの実行環境

シェルは実行時に以下の情報を保持します。

- 作業ディレクトリ
- 開いているファイル記述子
- ファイル作成マスク (link:_umask.html[umask])
- 受信時の挙動が ``無視'' に設定されたシグナルの集合 (link:_trap.html[trap])
- link:params.html#variables[環境変数]
- リソース制限 (link:_ulimit.html[ulimit])

これらの情報はシェルが起動されたときに元のプログラムからシェルに受け継がれます。そしてシェルが起動する外部コマンドやサブシェルにもシェルから受け継がれます。

これらの情報は所定の組込みコマンド等を使って変更可能です。

[[subshell]]
=== サブシェル

dfn:[サブシェル]は、実行中のシェルのプロセスのコピーです。サブシェルは括弧 +( )+ で囲んだ{zwsp}link:syntax.html#grouping[グルーピング]や{zwsp}link:syntax.html#pipelines[パイプライン]で使われます。

サブシェルはシェルのプロセスのコピーであるため、上記の情報の他にシェルで定義された関数やエイリアスなどの情報も元のシェルから受け継ぎます。ただし、

- link:_trap.html[Trap 組込みコマンド]で登録したトラップの設定は、(受信時の挙動が ``無視'' のものを除き) サブシェルではすべて解除されます。(注)
- サブシェルでは{zwsp}link:interact.html[対話モード]と{zwsp}link:job.html[ジョブ制御]は解除され、元のシェルのジョブの情報は受け継がれません。

サブシェルは元のシェルとは独立しているため、サブシェルでの作業ディレクトリの変更や変数代入は元のシェルに影響しません。

[NOTE]
Yash 以外のシェルでは、サブシェル内で実行されるコマンドが trap 組込みコマンドのみの場合にはトラップの設定を解除しないものもあります。

// vim: set filetype=asciidoc expandtab:
