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


12

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

継続的に実行させるために、私はwhile true; このような:

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

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

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

更新1: 以下の@InfectedRootの回答に基づいて、私は次の戦略を使用して問題を解決することができました。最初の使用

ps -aux | grep script_name

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

これは機能しますが、これは厄介なアプローチだと思います。より良い答えを探しています。

更新2: 答えは、2つのプロセスの強制終了で構成されています。コマンドラインでスクリプトを実行すると、2つのプロセスが開始されます。1つはスクリプト自体もう 1 つはinotifyプロセスです。



2
-9オプションを削除してkill使用するだけでkill、混乱が解消されます。
2014

1
ブラボー。もう一度 '$$'を駄洒落なキャッシュボックスに入れるために、ここでは-9オプションが完全になることを強調したいと思いkillます。:P
構文エラー'15

完全を期すために、両方のプロセスを一度に強制終了するクリーンなアプローチがあります。プロセスを個別に強制終了する代わりに、スクリプトのプロセスグループを強制終了して、それらを一度に強制終了できます。PGIDは、シェルスクリプトのメインプロセスのpidなどとの同じであるkill:あなたがマイナスとPGID接頭辞ことkill -- -<pgid>kill -9 -<pgid>など
cg909

回答:


6

改善するには、を使用し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

上記のソリューションは機能しますが、1行のコマンドでは機能しません。確認してください。エラーが発生する:プロセスなし
light94

@ light94 Error: no processは、あなたがすでにそれを殺したことを意味するだけです。VLCGeanyのような他の2つのプログラムを開いて、代わりにそれらを試すことで、テストできます。
cremefraiche 2014

ちょっと@cremefraiche、earlerがあなたのコードが機能することを言ったように....しかし、私は代替の解決策を探しており、私が目にする最も一般的な解決策はkill <pid>を使用することです。スクリプトがそのように終了しないので、コードが不適切かどうか少し心配ですか?案内してもらえますか?
light94

あなたは上で保存することができgrep -v grep回してgrep script_namegrep -e "[s]cript_name"
nmichaels

3

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

# jobs

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

# fg 1 

ここに画像の説明を入力してください

前面に表示されます。

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

ps -aux | grep script_name

ここに画像の説明を入力してください

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

sudo kill -9 pid_number_here

2
スクリプトを記述した後でシェルを閉じ、jobsコマンドが同じシェルでのみ機能すると思うので、最初のオプションは私のケースでは無効です。また、2つ目のオプションを試したところ、プロセスが強制終了されましたが、pgrep inotifywaitを実行しても、プロセスとして表示されます。
light94

シェルを閉じても、jobsはジョブのリストを表示します。それでもプロセスは終了します。
Babin Lonston、2014

2
私はそれを試しましたが、しませんでした。
light94 14

@ light94あなたは正しいです。ここでは頻繁に、jobsコマンドがシェル実行中のジョブのみを表示することがよくありました。特定のターミナルウィンドウを閉じた後、それらにアクセスする唯一の方法は経由でしたps(上記については上記を参照)。ですから、あなたがこれを作り上げていないことは確かです。
syntaxerror

2

ps+ grepまたはpgrepを使用して、プロセス / 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 プロセス名も出力します。

上記を実行した後、それぞれで使用エラーが発生します。スクリプト名をコピーして貼り付け、置き換えます。出力としてkillの使用法を取得
light94

script_nameは実行されていますか?それは私(debian jessie)のために動作します
Hongxu Chen

はい、実行されています。あなたの解決策は上記の@cremefraicheの解決策とほとんど同じなので、私は同じように機能することを期待していました:/
light94

はい、可能性のある方法、特にpgrep/を追加する方法を少し要約していましたpkill。それでも問題が解決しない場合は、pgrep-xなしで試してみてください。基本的に、他のプロセスが一致しているかどうかを確認せずに、1つの行コマンド内でプロセスを強制終了することは適切ではないと思います。
Hongxu Chen、2014

1

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

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

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

screen -d -m / path / to / program

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

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

screen -ls

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

画面-r

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

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


しかし、これらの問題における私の個人的な好みは、プロセスの開始と停止を管理する制御プログラムを持つことです。これは多少複雑になり、間違いなく複雑なスクリプトが必要になります。そして、他のスクリプトと同様に、それを行うには数十の良い方法があります。アプリケーションの起動と停止に日常的に使用する方法のbashの例を含めました。タスクが単純な場合は、制御スクリプトに直接挿入するか、この制御スクリプトで別の外部プログラムを呼び出すことができます。この例は、プロセスの管理に関して決して包括的なものではないことに注意してください。「開始」オプションを使用するときにスクリプトがまだ実行されていないことを確認し、実行中のPIDが実際に開始したプロセスであることを検証します(たとえば、スクリプトが tが終了し、同じ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

私はあなたが提案した「スクリーン」方法を試しました、そしてそれは美しく動きます。2つ目の方法はまだ試していません。答えてくれてありがとう。
light94
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.