execは常にシェルスクリプトの最後の行でなければなりませんか?

execは常にシェルスクリプトの最後の行でなければなりませんか?

以下を実行するシェルスクリプトがあります。

export FOO=foo           # step 1
/usr/bin/java my-server  # step 2

シェルスクリプトは、ステップ2で開始されたプロセスのPIDを知る必要がある親プログラムによって開始されます。現在、これはexecサーバーで実行コマンドを実行してシェルを手順2のコマンドに置き換えることによって行われます。したがって、シェルスクリプトのPIDとスクリプトを実行するプロセスは、「my-server」プロセスのPIDになります。

手順2で起動したサーバーをすばやく確認するには、このシェルスクリプトに別のコマンドを追加する必要があります。今私のプログラムは次のようになります。

export FOO=foo                      # step 1
exec /usr/bin/java my-server &      # step 2
/usr/bin/java my-validation         # step 3

上記の手順3を追加できますか?このアプローチの代替案は何ですか?

答え1

他の回答とコメントを組み合わせて、以下を追加します。

  • exec your_command &無意味。コマンドラインは非同期サブシェルで実行され、デフォルトでexecは無視されます。
    • それはわずかな単純化する。私たちが言うなら
      コマンド1;コマンド2)&
      その後、非同期サブシェルで実行されます(完了したら)。しかし、私たちが言うならcommand1command2command2command1
      実装する コマンド1;コマンド2)&
      これは実行されますが、実行されません。command1command2
  • リダイレクトだけでなく、スクリプトで正しく設定されたコマンド execに従うすべてのエントリは無視されます。exec失敗しても、後続の行を実行せずにシェルが終了します(command exec ...またはオプションbashを使用して、失敗した場合にシェルが終了するのをexecfail防ぐことができます)。exec
  • inまたはまたはなどexecのサブシェルで実行されている場合、そのサブシェルのみが終了します。exec cmd &exec cmd | cmd2(exec cmd3)
  • 確認コマンドでサーバープロセスのPIDを知る必要がある場合は、幸運ではないかもしれません。
  • そうでなく、サーバーが起動するまでverifyコマンドが自動的にしばらく待つ場合は、次のようにします。

    export FOO=foo                      # step 1
    /usr/bin/java my-validation &       # step 1½
    exec /usr/bin/java my-server        # step 2
    
  • それ以外の場合はこれを行うことができます

    export FOO=foo                              # step 1
    (sleep 10; /usr/bin/java my-validation) &   # step 1½
    exec /usr/bin/java my-server                # step 2
    

    警告:「my-server」プロセスがすぐに終了すると、「my-validation」コマンドが実行される前にスクリプトが終了する可能性が高く、親プロセスは制御を取り戻して実行を再開します。親プロセスと「my-validation」コマンドの両方が同じ場所に出力を書き込むと、混在する可能性があります。

    ただし、親プロセスがスクリプトが終了するのを待たないと、これは問題にならない可能性があります。

答え2

exec programスクリプトのプロセスをnewに置き換えるので、program意味がある場合にのみ使用してください。つまり、現在のスクリプトが消え、後続のコマンドを実行できません(exec program失敗しない限り)。

exec programシェルスクリプトの最後の行は、program長い間実行されたときに役に立ち、役に立たないシェルプロセスが終了するまでプロセスツリーに残ることを望まないからですprogram。または他の処理ツールを使用して、pstree次の違いを確認してください。

#!/bin/sh
echo $$
sleep 9999

そして

#!/bin/sh
echo $$
exec sleep 9999

exec program後で他のシェルコマンドを実行したい場合はまったくexec役に立ちません。届かないexec失敗しない限り)。

実際に起動後に何かを確認するのは非常に面倒です。/usr/bin/java my-server始めるのに時間がかかるからです。 (Javaはこれに関して非常に遅くなる可能性があります。Javaプロセスが使用可能になるか、自己終了するのに45秒以上かかることを覚えています...)システム使用量によって異なります。度またはスケジュールによっては、検証プロセスがmy-server前後に実行されることがあります。クルーガーとして、次の方法で検証ステップを遅らせることができます。

export FOO=foo
/usr/bin/java my-server &
sleep 10
exec /usr/bin/java my-validation

セルフスタートには10​​秒かかりますがmy-server、これはシステムの使用量とmy-server起動にかかる時間によっては十分である可能性があります。今回も、Javaプロセスを開始するのに45秒以上かかり、使用量の多いシステムでは長くかかることがわかりました。立ち上がって実行されるのを待つのに十分スマートであれば、これはsleep必要ないかもしれません。my-validationmy-server

ただサーバーを起動する方が合理的かもしれません。

#!/bin/sh
export FOO=foo
exec /usr/bin/java my-server

サービスが実行されていることを確認してください。モニタリングフレームワークを通じて、他の場所。

別のアプローチは、systemdシェルスクリプトよりも優れたプロセス制御を提供する他の最新のinitフレームワークと統合することです。たとえば、プロセスがsystemd正しく開始されたかどうかに関する情報を返したり、systemdさまざまな条件でサービスを自動的に再起動したりできます。

関連情報