Membunuh skrip shell yang berjalan di latar belakang

12

Saya telah menulis skrip shell untuk memonitor direktori menggunakan utilitas inotifyt dari inotifyt-tools. Saya ingin agar skrip berjalan terus-menerus di latar belakang, tetapi saya juga ingin dapat menghentikannya saat diinginkan.

Untuk membuatnya berjalan terus menerus, saya menggunakan while true; seperti ini:

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

Saya telah menyimpannya dalam file /bindan membuatnya dapat dieksekusi. Untuk membuatnya berjalan di latar belakang, saya menggunakan nohup <script-name> &dan menutup terminal.

Saya tidak tahu bagaimana cara menghentikan skrip ini. Saya telah melihat jawaban di sini dan pertanyaan yang sangat terkait di sini .

UPDATE 1: Atas dasar jawaban @InfectedRoot di bawah ini, saya dapat menyelesaikan masalah saya menggunakan strategi berikut. Penggunaan pertama

ps -aux | grep script_name

dan gunakan sudo kill -9 <pid>untuk membunuh proses. Saya kemudian harus pgrep inotifywaitdan menggunakan sudo kill -9 <pid>lagi untuk id yang dikembalikan.

Ini berfungsi tetapi saya pikir ini adalah pendekatan yang berantakan, saya mencari jawaban yang lebih baik.

UPDATE 2: Jawabannya terdiri dari membunuh 2 proses . Ini penting karena menjalankan skrip pada baris perintah memulai 2 proses, 1 skrip itu sendiri dan 2, proses inotify .

cahaya94
sumber
2
Menghapus -9opsi dari killdan menggunakan hanya killakan menghilangkan kekacauan itu.
Sree
1
Bravo. Untuk memasukkan '$$' ke dalam kotak uang tunai lagi, saya ingin menekankan bahwa -9opsi akan menjadi total di killsini. : P
syntaxerror
Demi kelengkapan: Ada pendekatan bersih untuk membunuh kedua proses sekaligus: Alih-alih membunuh proses secara terpisah, Anda dapat membunuh grup proses skrip untuk membunuh mereka sekaligus. Pgid adalah sama dengan pid dari proses utama shell script dan untuk killitu Anda awalan pgid dengan minus: kill -- -<pgid>, kill -9 -<pgid>, dll
cg909

Jawaban:

6

Untuk meningkatkan, menggunakan killall, dan juga menggabungkan perintah:

ps -aux | grep script_name
killall script_name inotifywait

Atau lakukan semuanya dalam satu baris:

killall `ps -aux | grep script_name | grep -v grep | awk '{ print $1 }'` && killall inotifywait
Cremefraiche
sumber
Solusi Anda di atas berfungsi tetapi bukan perintah satu baris. Bisakah Anda memeriksanya. Saya mendapatkan kesalahan: tidak ada proses
light94
@ light94 Error: no processseharusnya hanya berarti bahwa Anda sudah membunuhnya. Anda dapat mengujinya dengan membuka dua program lain, seperti VLC & Geany , dan sebagai gantinya mencobanya.
cremefraiche
Hai @cremefraiche, seperti kata earler kode Anda berfungsi .. tapi saya telah mencari solusi alternatif dan solusi paling umum yang saya lihat adalah menggunakan kill <pid>. Karena skrip saya tidak berakhir seperti itu, saya agak khawatir jika kode saya tidak tepat? Bisakah Anda membimbing saya?
light94
Anda dapat menghemat grep -v grepdengan mengubahnya grep script_namemenjadi grep -e "[s]cript_name".
nmichaels
3

Daftar pekerjaan latar belakang menggunakan

# jobs

Kemudian pilih jumlah pekerjaan sebelumnya dan jalankan

contoh

# fg 1 

masukkan deskripsi gambar di sini

akan membawa ke latar depan.

Kemudian bunuh menggunakan CTRL + C atau cara yang lebih mudah untuk menemukan PID skrip menggunakan

ps -aux | grep script_name

masukkan deskripsi gambar di sini

Kemudian bunuh menggunakan pid

sudo kill -9 pid_number_here
Babin Lonston
sumber
2
Opsi pertama tidak berlaku untuk kasus saya karena saya menutup shell setelah menyatakan script dan saya pikir perintah pekerjaan hanya berfungsi untuk shell yang sama. Juga, saya mencoba opsi kedua dan itu membunuh proses tetapi ketika saya melakukan pgrep inotifywait, saya masih bisa melihatnya sebagai proses di sana.
light94
pekerjaan akan memberikan daftar pekerjaan bahkan ketika Anda menutup shell. Masih proses mendapatkan selesai itu akan ada di sana.
Babin Lonston
2
Saya mencobanya tetapi ternyata tidak.
light94
@ light94 Anda benar. Sering terjadi di sini bahwa jobsperintah hanya menampilkan pekerjaan yang sedang berjalan di shell. Setelah saya menutup jendela terminal tertentu, satu-satunya cara untuk mengaksesnya adalah melalui ps(lihat di atas untuk itu). Jadi sudah pasti Anda tidak mengada-ada.
syntaxerror
2

Anda dapat menggunakan ps+ grepatau pgrepuntuk mendapatkan nama proses / pid ; nanti gunakan killall/ pkilluntuk membunuh nama proses atau gunakan killuntuk membunuh pid. Semua yang berikut harus bekerja.

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

Yang paling penting adalah Anda harus memastikan bahwa Anda hanya membunuh proses yang tepat yang ingin Anda bunuh.

pkillSaya lebih pgrepcocok dengan pola daripada nama pastinya , jadi lebih berbahaya; di sini -xditambahkan untuk mencocokkan nama yang tepat.

Juga, ketika menggunakan pgrep/ pkillAnda mungkin perlu

  • -funtuk mencocokkan baris perintah penuh (seperti ps auxhalnya)
  • -a untuk juga mencetak nama proses.
Hongxu Chen
sumber
Setelah mengeksekusi di atas, saya mendapatkan kesalahan penggunaan di masing-masing. saya menyalin menempel dan mengganti scriptname. Saya mendapatkan penggunaan untuk kill sebagai output
light94
Apakah script_name berjalan? Ini bekerja untuk saya (debian jessie)
Hongxu Chen
ya itu sedang berjalan. dan solusi Anda hampir sama dengan solusi @cremefraiche di atas, jadi saya mengharapkannya bekerja dengan cara yang sama: /
light94
Ya, saya agak meringkas cara yang mungkin untuk melakukannya, terutama menambahkan pgrep/ pkill. Jika itu masih salah Anda bisa mencoba pgreptanpa -x. Pada dasarnya saya tidak berpikir itu baik untuk membunuh proses dalam satu perintah baris tanpa memeriksa apakah proses lain cocok.
Hongxu Chen
1

Seperti yang Anda mungkin tahu ada banyak cara untuk melakukan ini.

Mengenai "PEMBARUAN # 2" Anda - secara umum, menghentikan proses apa pun dalam hierarki orang tua-anak umumnya akan menghentikan semua proses yang terkait. Tetapi ada banyak pengecualian untuk ini. Idealnya, Anda ingin mengakhiri 'anak' terakhir di pohon proses, maka orang tua dari anak ini harus keluar jika mereka tidak memiliki tugas lain untuk dijalankan. Tetapi jika Anda membunuh orang tua, sinyal harus diteruskan ke anak-anak ketika orang tua meninggal dan anak-anak harus keluar juga - tetapi ada kasus di mana proses anak-anak dapat mengabaikan sinyal (melalui perangkap atau mekanisme serupa) dan dapat melanjutkan untuk menjalankan akan diwarisi oleh proses 'init' (atau serupa.) Tapi subjek perilaku proses ini bisa menjadi kompleks dan saya hanya akan membiarkannya di sana ...

Salah satu metode yang saya suka jika saya tidak ingin menggunakan skrip kontrol (dijelaskan selanjutnya) adalah dengan menggunakan utilitas 'layar' untuk memulai dan mengelola proses. Perintah 'layar' penuh dengan fitur dan dapat membutuhkan waktu untuk dikuasai. Saya mendorong Anda untuk membaca halaman manual 'layar' untuk penjelasan lengkap. Contoh cepat untuk memulai proses di latar belakang adalah perintah:

layar -d -m / path / ke / program

Ini akan memulai "/ path / ke / program" di dalam sesi 'layar'.

Anda dapat melihat sesi lari Anda dengan perintah:

layar -l

Dan kapan saja Anda dapat menyambung kembali ke program Anda yang sedang berjalan dengan perintah:

layar -r

Dan kemudian akhiri saja dengan ^ C atau apa pun.

Selain keindahan dari dapat terhubung kembali dan terputus dari proses Anda sesuka hati adalah 'layar' akan menangkap stdout () apa pun yang mungkin dihasilkan oleh program Anda.


Tetapi preferensi pribadi saya dalam masalah ini adalah memiliki program kontrol yang mengelola awal dan penghentian suatu proses. Ini bisa menjadi agak rumit dan memerlukan beberapa skrip yang bisa dibilang rumit. Dan seperti naskah apa pun, ada lusinan cara bagus untuk melakukannya. Saya telah memasukkan contoh bash dari metode yang saya gunakan secara rutin untuk memulai dan menghentikan aplikasi. Jika tugas Anda sederhana, Anda dapat memasukkannya langsung ke skrip kontrol - atau skrip kontrol ini memanggil program eksternal lain. Perhatikan bahwa contoh ini sama sekali tidak komprehensif dalam hal mengelola proses. Saya telah menghilangkan kemungkinan skenario seperti: Memastikan skrip belum berjalan ketika Anda menggunakan opsi "mulai", memvalidasi bahwa PID yang sedang berjalan sebenarnya adalah proses yang Anda mulai (mis. Skrip Anda belum t mati dan proses lain telah dimulai menggunakan PID yang sama), dan memvalidasi bahwa skrip benar-benar merespons (keluar) pada permintaan 'bunuh' pertama. Melakukan semua pemeriksaan ini dapat menjadi rumit dan saya tidak ingin membuat contoh terlalu panjang dan rumit. Anda mungkin ingin memodifikasi contoh untuk mempraktikkan skrip shell Anda.

Simpan kode berikut ke file yang disebut "programctl", membuatnya dapat dieksekusi dengan perintah:

chmod 755 programctl

Kemudian edit file dan tambahkan kode / skrip Anda di bagian case yang dimulai dengan "myscript".

Setelah semuanya ada, dengan asumsi "programctl" ada di direktori saat ini, Anda dapat memulai program Anda dengan:

./programctl mulai

Dan hentikan dengan:

./programctl berhenti

Bersulang.

#!/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
Christopher Morris
sumber
Saya mencoba metode "layar" yang Anda sarankan dan berfungsi dengan baik. Saya belum mencoba metode kedua. Terima kasih atas jawabannya.
light94