web-dev-qa-db-ja.com

バックグラウンドで実行されているシェルスクリプトを強制終了する

Inotifyt-toolsのinotifywaitユーティリティを使用してディレクトリを監視するシェルスクリプトを作成しました。このスクリプトをバックグラウンドで継続的に実行したいのですが、必要に応じてスクリプトを停止できるようにしたいと思っています。

継続的に実行するには、while trueを使用しました。このような:

while true;
do #a set of commands that use the inotifywait utility
end

/binのファイルに保存し、実行可能にしました。バックグラウンドで実行するために、Nohup <script-name> &を使用してターミナルを閉じました。

このスクリプトを停止する方法がわかりません。私は回答 here および非常に密接に関連する質問 here を見てきました。

UPDATE 1:以下の@InfectedRootの回答に基づいて、次の戦略を使用して問題を解決できました。最初の使用

ps -aux | grep script_name

そしてSudo kill -9 <pid>を使用してプロセスを強制終了します。次に、pgrep inotifywaitを返して、返されたIDに再度Sudo kill -9 <pid>を使用する必要がありました。

これはうまくいきますが、これは面倒なアプローチだと思います。もっと良い答えを探しています。

UPDATE 2:答えは2つのプロセスを殺すことで構成されます。コマンドラインでスクリプトを実行すると2つのプロセスが開始されるため、これは重要です。1つはscript自体で、2つはです。 inotifyプロセス

12
light94

改善するには、killallを使用し、コマンドを組み合わせます。

ps -aux | grep script_name
killall script_name inotifywait

または、すべてを1行で実行します。

killall `ps -aux | grep script_name | grep -v grep | awk '{ print $1 }'` && killall inotifywait
6
cremefraiche

を使用してバックグラウンドジョブを一覧表示する

# jobs

次に、前のジョブ数を選択して実行します

# fg 1 

enter image description here

前面に表示されます。

次に、CTRL + Cを使用してそれを強制終了するか、使用してスクリプトのPIDを見つける簡単な方法

ps -aux | grep script_name

enter image description here

次に、pidを使用して殺します

Sudo kill -9 pid_number_here
3
Babin Lonston

ps + grepまたはpgrepを使用してプロセスを取得できますname/pid;後でkillall/pkillを使用してプロセス名を強制終了するか、killを使用してpidを強制終了します。以下のすべてが機能するはずです。

killall $(ps aux | grep script_name | grep -v grep | awk '{ print $1 }') && killall inotifywait
(ps -ef | grep script_name | grep -v grep | awk '{ print $1 }' | xargs killall) && killall inotifywait
(ps -ef | grep script_name | grep -v grep | awk '{ print $2 }' | xargs kill) && killall inotifywait
(pgrep -x script_name | xargs kill) && pkill -x inotifywait
pkill -x script_name && pkill -x inotifywait

最も重要なことは、あなたが殺したいと思っている正確なプロセスだけを殺すことを確実にするべきだということです。

pkill/pgrep正確な名前ではなくパターンと一致するため、より危険です。ここに-xが正確な名前と一致するように追加されます。

また、pgrep/pkillを使用する場合、

  • -fは、コマンドライン全体と一致します(ps auxと同様)
  • -aは、プロセス名も出力します。
2
Hongxu Chen

おそらくお分かりのように、これを行う方法はたくさんあります。

「UPDATE#2」について-一般的に言えば、親子階層のプロセスを終了すると、通常、関連するすべてのプロセスが終了します。しかし、これには多くの例外があります。理想的には、プロセスツリーの最後の「子」を終了したい場合、実行する他のタスクがない場合、この子の親は終了する必要があります。ただし、親を殺した場合、親が死んだときにシグナルが子に中継され、子も終了する必要があります。ただし、子プロセスが(トラップまたは同様のメカニズムを介して)シグナルを無視して続行する場合があります。を実行するには、「init」プロセス(または類似のプロセス)が継承します。ただし、このプロセス動作の対象は複雑になる可能性があるため、そのままにしておきます...

制御スクリプト(次で説明)を使用したくない場合に好む方法の1つは、「screen」ユーティリティを使用してプロセスを開始および管理することです。 「screen」コマンドは機能が豊富で、習得に時間がかかる場合があります。完全な説明については、「screen」のマニュアルページを読むことをお勧めします。バックグラウンドでプロセスを開始する簡単な例は、次のコマンドです。

screen -d -m/path/to/program

これにより、「画面」セッション内で「/ path/to/program」が開始されます。

次のコマンドで実行中のセッションを確認できます。

screen -ls

また、次のコマンドでいつでも実行中のプログラムに再接続できます。

画面-r

そして、それを^ Cなどで終了します。

プロセスから自由に再接続および切断できることの美しさに加えて、 'screen'はプログラムが生成する可能性のあるstdout()をキャプチャします。


しかし、これらの問題における私の個人的な好みは、プロセスの開始と停止を管理する制御プログラムを持つことです。これはやや複雑になる可能性があり、間違いなく複雑なスクリプトを必要とします。そして、他のスクリプトと同様に、それを行うには数十の良い方法があります。アプリケーションの起動と停止に日常的に使用するメソッドのbashの例を含めました。タスクが単純な場合は、制御スクリプトに直接挿入するか、この制御スクリプトで別の外部プログラムを呼び出すことができます。この例は、プロセスの管理に関して決して包括的なものではないことに注意してください。次のようなシナリオの可能性は省きました。「開始」オプションを使用するときにスクリプトがまだ実行されていないことを確認し、実行中のPIDが実際に開始したプロセスであることを検証します(たとえば、スクリプトが停止しておらず、同じPIDを使用して別のプロセスが開始され、スクリプトが最初の「kill」要求で実際に応答(終了)したことを検証します。これらすべてのチェックを行うと複雑になる可能性があるため、この例を長く複雑にしたくありませんでした。例を変更して、シェルスクリプトを練習することもできます。

次のコードを「programctl」というファイルに保存し、次のコマンドで実行可能にします。

chmod 755 programctl

次に、ファイルを編集し、「myscript」で始まるケースセクションにコード/スクリプトを追加します。

すべてが整ったら、「programctl」が現在のディレクトリにあると想定して、次のコマンドでプログラムを開始できます。

./programctl start

そしてそれを止める:

./programctl stop

乾杯。

#!/bin/bash
# Description:  A wrapper script used to stop/start another script.

#--------------------------------------
# Define Global Environment Settings:
#--------------------------------------

# Name and location of a persistent PID file

PIDFILE="/tmp/tmpfile-$LOGNAME.txt"

#--------------------------------------
# Check command line option and run...
# Note that "myscript" should not
# provided by the user.
#--------------------------------------

case $1
in
    myscript)
        # This is where your script would go.
        # If this is a routine 'bash' Shell script, you can enter
        # the script below as illustrated in the example.  
        # Or you could simply provide the path and parameters
        # to another script such as /dir/name/command -options

        # Example of an embedded script:

        while true
        do
            # do something over and over...
            sleep 1
        done

        # Example of an external script:

        /usr/local/bin/longrun -x
    ;;

    start)
        # Start your script in the background.
        # (Note that this is a recursive call to the wrapper
        #  itself that effectively runs your script located above.)
        $0 myscript &

        # Save the backgound job process number into a file.
        jobs -p > $PIDFILE

        # Disconnect the job from this Shell.
        # (Note that 'disown' command is only in the 'bash' Shell.)
        disown %1

        # Print a message indicating the script has been started
        echo "Script has been started..."
    ;;

    stop)
        # Read the process number into the variable called PID
        read PID < $PIDFILE

        # Remove the PIDFILE
        rm -f $PIDFILE

        # Send a 'terminate' signal to process
        kill $PID

        # Print a message indicating the script has been stopped
        echo "Script has been stopped..."
    ;;

    *)
        # Print a "usage" message in case no arguments are supplied
        echo "Usage: $0 start | stop"
    ;;
esac
1