web-dev-qa-db-ja.com

シェルスクリプト全般、特にBashにはどのような終了モードがありますか?

シェルスクリプトでは、「終了」は通常、自発的または少なくとも正常にセッションを終了することを意味することを知っています( またはプロセス セッション内)およびいくつかの異なる終了モードがあること。以下は私が知っているこれらです:

1.単純なexitコマンド

私が最初のシェルセッション(シェルセッション0)にいる場合、通常はシェルCLIウィンドウが閉じますが、一部のサブセッション(シェルセッション1以降など)では、実行すると通常、ユーザーが前のセッションに戻るだけです(たとえば、_1 → 0_)。

2. _exit SOME_EXIT-CODE_コマンド

このような終了で使用される3つの主要な終了コードが見つかりました。

  1. _exit 0_(成功)。
  2. _exit 1_(「ゼロ除算」やその他の許可されない操作などの一般的なエラー)。
  3. _exit 2_(Bash 4.x.xのように-シェルビルトインの誤用。例としては、空の関数; myFunc() {})。

これらは、実行の結果の指標としてコマンドシーケンスの最後に追加されることがよくあります。 ユニットテスト の一部として、次のようになります。

_domain="$1" && test -z "$domain" && exit 2 
# Test if a user passes only one domain as a parameter, when executing this script
_

3.追加されていないスクリプト出口

私が間違っていなければ、Bashスクリプトの実行が終了すると、その「終了」は実際には一般的な* nix用語で「終了」します。スクリプト自体は、ユーザーが終了してCLIセッションに戻るセッションです。ここでも、いくつかの終了コードが与えられる場合があります。

私の質問

シェルスクリプト全般、特にBashに他の「終了モード」はありますか?

2
user149572

「終了」とは、通常、自発的または少なくとも正常に終了することを意味します

少なくともPOSIXテキストは、外部の理由で殺されるのではなく、プロセスの自発的な終了のためだけにexitを使用しているようです。 (例: wait() を参照)シグナルによって強制終了されたプロセスは成功とは見なされないため、成功した終了はその意味で「終了」である必要があります。これらの用語は、非公式な使用ではそれほど厳密には使用されないと思いますが。

シェルスクリプト全般、特にBashに他の「終了モード」はありますか?

Modeは、一部のコンテキスト(chmod()など)で特定の技術的意味を持っていますが、ここでは考えられないので、私は ' mあなたが何を求めているのか正確にはわかりません。

いずれにせよ、シェルスクリプトは 出口 少なくとも次の理由で終了します。

  1. スクリプトはスクリプトの最後まで実行されます。スクリプトの終了ステータスは、最後に実行されたコマンドの終了ステータスです。
  2. スクリプトは、引数なしで exit組み込みコマンド を実行します。この場合も、終了ステータスは最後に実行されたコマンドのステータスです。
  3. スクリプトは、引数を指定してexitコマンドを実行します。終了ステータスは引数の値です。
  4. _set -u_/_set -o nounset_ が有効な間、スクリプトは未設定の変数を参照します。終了ステータスはシェルによって異なりますが、ゼロ以外です。 (Bashは_127_を使用しているようです。)(*)
  5. スクリプトは、 _set -e_/_set -o errexit_ が有効なときに失敗するコマンドを実行します。終了ステータスは、失敗したコマンドのステータスです。 (ただし、_set -e_の問題については、 BashFAQ 105 を参照してください。)
  6. スクリプトで構文エラーが発生します。シェルの終了ステータスがゼロ以外です。 (Bashは_1_を使用しているようです。)(*)
  7. スクリプトは シグナル を受信し、スクリプトを終了させます。すべてのシグナルが終了を引き起こすわけではなく、シグナルを無視するか、スクリプト内で trap組み込みコマンド を使用してハンドラーを設定できます。これはe.qにも当てはまります。ヒッティング Ctrl-CSIGINT信号を送信します。(*)

技術的な意味では、ケース1から6の場合、スクリプトを実行しているシェルプロセスは自発的に終了します(つまり、プロセスはexit()を呼び出します) 。一方、script自体の観点からは、_set -e_、_set -u_または構文が原因で終了しますエラーは非自発的と呼ばれることもあります。ただし、シェルスクリプトはシェルインタープリターと同じではありません。

1から3では、正常に完了するにはゼロの終了ステータスを使用し、失敗する場合はゼロ以外の値を使用するのが習慣です。ゼロ以外の値の正確な意味は、ユーティリティによって異なります。ゼロと1のみを使用するものもあれば、状況ごとに異なるゼロ以外のステータスを使用するものもあります。たとえば、grepは_1_を使用して一致が見つからなかったことを示し、_1_より大きい値はエラーを示します。 Bashのビルトインも_2_を使用して、無効なオプションなどのエラーを示します。同様のカスタムを使用すると便利な場合がありますが、スクリプトの終了ステータスが何を意味するかを文書化する必要があります。終了ステータスは通常8ビットに制限されているため、範囲は_0_から_255_であることに注意してください。

4から6では、状況は通常、ある種の障害と見なされるため、終了ステータスはゼロ以外です。 7では、終了ステータスはありません。代わりに、シグナルが原因でプロセスが終了すると、wait()システムコールは問題のシグナルを示します。親プロセスがシェルの場合、通常、終了ステータスは_128 + <signal number>_で表されます。例: SIGTERMで終了する子の場合は_143_。

(*スクリプトとは異なり、対話型シェルは構文エラーまたは_set -u_またはSIGINTのために終了しません。)

最初のシェルセッションに参加している場合、通常はシェルCLIウィンドウが閉じます。

ターミナルエミュレータは通常、開始したプロセスが終了すると閉じます。しかし、それはターミナルエミュレーター次第であり、シェルの機能ではありません。ターミナルエミュレータは、ウィンドウを開いたままにして、プログラムが終了したことをユーザーに通知する場合があります。また、ターミナルエミュレータ内でシェル以外のものを実行することもできます。

サブセッションに参加している場合、実行は通常、ユーザーを前のセッションに戻すだけです。

対話型シェルを使用して別のシェルを開始する場合、子が終了しても親シェルは続行します。ただし、これはシェルとは関係ありません。エディターを起動したり、対話型シェルからコマンドを実行したりした場合にも同じことが起こります。子プロセスが終了すると、親シェルは引き続きユーザーからのコマンドを受け入れます。

Bashは、Bashが起動するたびに1ずつ増加する変数SHLVLを保持しているため、ある意味では、ネストされたシェルの内部的な考え方があります。しかし、「サブセッション」というフレーズはあまり一般的ではないと思います。ましてや、そのような番号付けは言うまでもありません。 (SHLVLは_1_で初期化されると思います。)

6
ilkkachu

終了の実際には異なる「モード」はありません。

引数なしでexitを使用すると、前のコマンドの戻りコードが戻りコードとして使用されます。これがスクリプトの最後のコマンドである場合、スクリプトがsourcedでない限り、事実上NOOPです。

exit 0は、戻りコードを0に明示的に設定しています。これは、エラー以外の状態の標準終了コードです。

0以外の数値を使用することは、単に同じことを実行します。つまり、戻りコードを指定された値に設定します。

スクリプトがexit命令なしで終了すると、コマンドによって実行された最後のコマンドの終了コードが使用されます。

trap '[...]' EXITを実行し、トラップにexitステートメントを含めると、スクリプトが実際に終了する前にトラップが実行されるため(含まれているexitコマンドを含む)、これに優先します。

3
DopeGhoti

0、1、および2だけでなく、さらに多くの終了コードがあります。参照:

終了コードについて考えると、スクリプトは、疑わしい場所で終了するとは限らないことを覚えておく必要があります。それらが終了する前に、それらは信号で殺されるか、または計画外の入力ターミネータが押すことによって終了する可能性があります Ctrl-D、予期しない終了を生成します。したがって、スクリプト、特に多数のコードを返すスクリプトについては、十分な理解と分析が必要です。

1
user147505

質問を正しく理解したかどうかはわかりませんが、プログラムが終了ではなく終了というさまざまな方法を模索しているようです。

  • あなたが言ったようにシェル(またはシェルによって実行されるコマンド)を終了させることができます(例:EoFを奨励するとインタラクティブシェルは終了します。実行するコマンドが不足すると(スクリプトの終わり)、シェルも終了します。または出口に遭遇した場合(例:シェルの「exit2」はリターンコード2で終了します)

  • 親シェルが終了し、その親シェルからプロセスをデタッチするために「Nohup」(またはそのバリアント)が実行されなかった場合、「HUP」を受け取る可能性があります。

  • ctrl-Cを受け取ることができます

  • メモリ/ CPU /何か(fd?)が不足する可能性があります

  • キル-9またはその他を受け取る可能性があります(スクリプト自体は、たとえ奇妙な場合でも、そのように「キル-9 $$」することができます...)

  • シェルがパイプの右側にある可能性があります。パイプの左側が突然終了したために破損します。

  • そして、私はもっと多くの場合があるに違いない…。

それらのそれぞれがプログラム/シェルを終了する必要があり、それぞれが少し異なります(そして、終了する前に何かをするためにそれらのいくつかをトラップするか、完全に終了するのを防ぐことができます(ctrl-Cの場合、たとえば、INT信号をトラップすることができます) )

1
Olivier Dulac