Giết một kịch bản shell chạy trong nền


12

Tôi đã viết một tập lệnh shell để theo dõi một thư mục bằng tiện ích inotifywait của inotifyt-tools. Tôi muốn tập lệnh đó chạy liên tục trong nền, nhưng tôi cũng muốn có thể dừng nó khi muốn.

Để làm cho nó chạy liên tục, tôi đã sử dụng while true; như thế này:

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

Tôi đã lưu nó trong một tập tin /binvà làm cho nó thực thi được. Để làm cho nó chạy trong nền, tôi đã sử dụng nohup <script-name> &và đóng thiết bị đầu cuối.

Tôi không biết làm cách nào để dừng kịch bản này. Tôi đã xem xét các câu trả lời ở đây và một câu hỏi liên quan rất chặt chẽ ở đây .

CẬP NHẬT 1: Trên cơ sở câu trả lời của @InfectedRoot bên dưới, tôi đã có thể giải quyết vấn đề của mình bằng chiến lược sau. Lần dùng đầu tiên

ps -aux | grep script_name

và sử dụng sudo kill -9 <pid>để tiêu diệt các quá trình. Sau đó tôi đã phải pgrep inotifywaitvà sử dụng sudo kill -9 <pid>lại cho id trở lại.

Điều này hoạt động nhưng tôi nghĩ rằng đây là một cách tiếp cận lộn xộn, tôi đang tìm kiếm một câu trả lời tốt hơn.

CẬP NHẬT 2: Câu trả lời bao gồm giết 2 quá trình . Điều này rất quan trọng vì việc chạy tập lệnh trên dòng lệnh sẽ khởi tạo 2 quy trình, 1 chính tập lệnh và 2, quy trình inotify .



2
Loại bỏ -9tùy chọn khỏi killvà chỉ sử dụng killsẽ loại bỏ mớ hỗn độn ra khỏi nó.
Sree

1
Bravo. Để có được một số '$$' vào hộp tiền mặt một lần nữa, tôi muốn nhấn mạnh rằng -9tùy chọn này sẽ hoàn toàn killở đây. : P
cú pháp 15/12/14

Vì mục đích hoàn chỉnh: Có một cách tiếp cận rõ ràng để tiêu diệt cả hai quy trình cùng một lúc: Thay vì giết riêng các quy trình, bạn có thể giết nhóm quy trình của tập lệnh để tiêu diệt chúng cùng một lúc. Các pgid là giống như pid của tiến trình chính shell script và để killnó bạn tiền tố pgid với dấu trừ: kill -- -<pgid>, kill -9 -<pgid>vv
cg909

Câu trả lời:


5

Để cải thiện, sử dụng killallvà cũng kết hợp các lệnh:

ps -aux | grep script_name
killall script_name inotifywait

Hoặc làm mọi thứ trong một dòng:

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

Giải pháp trên của bạn hoạt động nhưng không phải là lệnh một dòng. Xin vui lòng bạn có thể kiểm tra nó. Tôi gặp lỗi: không có quy trình
light94 13/12/14

@ light94 Error: no processchỉ có nghĩa là bạn đã giết nó. Bạn có thể kiểm tra nó bằng cách mở hai chương trình khác, như VLC & Geany , và thử nó với chúng.
cremefraiche

Xin chào @cremefraiche, như Earler đã nói mã của bạn hoạt động .. nhưng tôi đã tìm kiếm các giải pháp thay thế và giải pháp phổ biến nhất tôi thấy là sử dụng kill <pid>. Vì, kịch bản của tôi không kết thúc theo cách đó, tôi hơi lo lắng nếu mã của tôi không đúng? Bạn có thể vui lòng hướng dẫn cho tôi?
ánh sáng94

Bạn có thể tiết kiệm grep -v grepbằng cách biến grep script_namethành grep -e "[s]cript_name".
nmichaels

3

Liệt kê các công việc nền bằng cách sử dụng

# jobs

Sau đó chọn số lượng công việc trước đó và chạy

thí dụ

# fg 1 

nhập mô tả hình ảnh ở đây

sẽ mang đến tiền cảnh.

Sau đó, giết nó bằng CTRL + C hoặc cách dễ dàng hơn để tìm PID của tập lệnh bằng cách sử dụng

ps -aux | grep script_name

nhập mô tả hình ảnh ở đây

Sau đó giết bằng pid

sudo kill -9 pid_number_here

2
Tùy chọn đầu tiên không hợp lệ cho trường hợp của tôi vì tôi đóng shell sau khi nêu tập lệnh và tôi nghĩ lệnh jobs chỉ hoạt động với cùng shell. Ngoài ra, tôi đã thử tùy chọn thứ hai và nó sẽ giết quá trình nhưng khi tôi thực hiện pgrep inotifywait, tôi vẫn có thể xem nó là một quá trình ở đó.
ánh sáng94

công việc sẽ đưa ra danh sách các công việc ngay cả khi bạn đóng vỏ. Vẫn là quá trình nhận được kết thúc nó sẽ ở đó.
Babin Lonston

2
Tôi đã thử nhưng không được.
ánh sáng94

@ light94 Bạn nói đúng. Nó xảy ra thường xuyên ở đây rằng jobslệnh thực sự chỉ hiển thị các công việc đang chạy trong shell. Khi tôi đóng một cửa sổ đầu cuối nhất định, cách duy nhất để truy cập chúng là thông qua ps(xem ở trên để biết điều đó). Vì vậy, chắc chắn rằng bạn không làm điều này.
cú pháp

2

Bạn có thể sử dụng ps+ grephoặc pgrepđể lấy tên quy trình / pid ; sau này sử dụng killall/ pkillđể giết tên tiến trình hoặc sử dụng killđể tiêu diệt pid. Tất cả những điều sau đây nên hoạt động.

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

Điều quan trọng nhất là bạn nên đảm bảo rằng bạn chỉ giết đúng quy trình chính xác mà bạn đang hy vọng giết.

pkill/ pgrepkhớp với một mẫu chứ không phải tên chính xác , vì vậy nguy hiểm hơn; ở đây -xđược thêm vào để khớp với tên chính xác.

Ngoài ra, khi sử dụng pgrep/ pkillbạn có thể cần

  • -fđể khớp với dòng lệnh đầy đủ (giống như ps aux)
  • -a để in tên quá trình.

Sau khi thực hiện ở trên, tôi nhận được một lỗi sử dụng trong mỗi. tôi đang sao chép dán và thay thế tên tập lệnh. Tôi nhận được sử dụng cho kill như đầu ra
light94 13/12/14

Script_name có chạy không? Nó hoạt động với tôi (debian jessie)
Hongxu Chen

vâng nó đang chạy và giải pháp của bạn gần giống như các giải pháp @cremefraiche ở trên, vì vậy tôi đã mong đợi nó hoạt động theo cùng một cách: /
light94

Vâng, tôi đã phần nào tóm tắt các cách có thể để làm như vậy, đặc biệt là thêm pgrep/ pkill. Nếu điều đó vẫn sai, bạn có thể thử pgrepmà không có -x. Về cơ bản tôi không nghĩ việc giết tiến trình trong một dòng lệnh là tốt mà không kiểm tra xem các quy trình khác có khớp hay không.
Hongxu Chen

1

Như bạn có thể nói có rất nhiều cách để làm điều này.

Về "CẬP NHẬT # 2" của bạn - nói chung, việc chấm dứt bất kỳ quy trình nào trong hệ thống phân cấp cha-con thường sẽ chấm dứt tất cả các quy trình liên quan. Nhưng có nhiều trường hợp ngoại lệ cho điều này. Lý tưởng nhất là bạn muốn chấm dứt 'đứa con' cuối cùng trong cây quy trình, sau đó cha mẹ của đứa trẻ này sẽ thoát nếu chúng không có nhiệm vụ nào khác để chạy. Nhưng nếu bạn giết cha mẹ, tín hiệu sẽ được truyền xuống cho trẻ em khi cha mẹ chết và con cái cũng thoát ra - nhưng có những trường hợp quá trình con có thể bỏ qua tín hiệu (thông qua bẫy hoặc cơ chế tương tự) và có thể tiếp tục để chạy một sẽ được kế thừa bởi quá trình 'init' (hoặc tương tự.) Nhưng chủ đề hành vi quy trình này có thể trở nên phức tạp và tôi sẽ để nó ở đó ...

Một phương pháp tôi thích nếu tôi không muốn sử dụng tập lệnh điều khiển (được mô tả tiếp theo) là sử dụng tiện ích 'màn hình' để bắt đầu và quản lý quy trình. Lệnh 'màn hình' có đầy đủ các tính năng và có thể mất một chút thời gian để làm chủ. Tôi khuyến khích bạn đọc trang man 'màn hình' để được giải thích đầy đủ. Một ví dụ nhanh để bắt đầu một quá trình trong nền sẽ là lệnh:

màn hình -d -m / đường dẫn / đến / chương trình

Điều này sẽ bắt đầu "/ path / to / chương trình" bên trong phiên 'màn hình'.

Bạn có thể thấy phiên chạy của mình bằng lệnh:

màn hình

Và bất cứ lúc nào bạn có thể kết nối lại với chương trình đang chạy của mình bằng lệnh:

màn hình -r

Và sau đó chỉ cần chấm dứt nó với một ^ C hoặc bất cứ điều gì.

Bên cạnh vẻ đẹp của việc có thể kết nối lại và ngắt kết nối khỏi quy trình của bạn theo ý muốn là 'màn hình' sẽ nắm bắt mọi thiết bị xuất chuẩn () mà chương trình của bạn có thể tạo ra.


Nhưng sở thích cá nhân của tôi trong những vấn đề này là có một chương trình kiểm soát quản lý việc bắt đầu và dừng quá trình. Điều này có thể hơi phức tạp và đòi hỏi một số kịch bản phức tạp được cho là. Và giống như bất kỳ kịch bản nào, có hàng tá cách tốt để làm điều đó. Tôi đã bao gồm một ví dụ bash về phương pháp tôi thường xuyên sử dụng để khởi động và dừng ứng dụng. Nếu tác vụ của bạn đơn giản, bạn có thể chèn nó trực tiếp vào tập lệnh điều khiển - hoặc bạn có thể có tập lệnh điều khiển này gọi một chương trình bên ngoài khác. Lưu ý rằng ví dụ này không có nghĩa là toàn diện về mặt quản lý quy trình. Tôi đã bỏ qua khả năng xảy ra các tình huống như: Đảm bảo rằng tập lệnh không chạy khi bạn sử dụng tùy chọn "bắt đầu", xác thực rằng PID đang chạy thực sự là quá trình bạn đã bắt đầu (ví dụ: tập lệnh của bạn không ' T đã chết và một quá trình khác đã được bắt đầu bằng cách sử dụng cùng một PID) và xác nhận rằng tập lệnh thực sự đã phản hồi (đã thoát) theo yêu cầu 'giết' đầu tiên. Thực hiện tất cả các kiểm tra này có thể trở nên phức tạp và tôi không muốn làm cho ví dụ quá dài và phức tạp. Bạn có thể muốn sửa đổi ví dụ để thực hành kịch bản shell của bạn.

Lưu mã sau vào một tệp có tên là "chương trình", làm cho nó có thể thực thi được bằng lệnh:

chmod 755 chương trình

Sau đó chỉnh sửa tệp và thêm mã / tập lệnh của bạn vào phần trường hợp bắt đầu bằng "myscript".

Khi mọi thứ đã ổn định, giả sử "chương trình" nằm trong thư mục hiện tại, bạn có thể bắt đầu chương trình của mình với:

./programctl bắt đầu

Và dừng lại với:

./programctl dừng

Chúc mừng.

#!/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

Tôi đã thử phương pháp "màn hình" mà bạn đề xuất và nó hoạt động rất đẹp. Tôi vẫn chưa thử phương pháp thứ hai. Cảm ơn bạn đã trả lời.
ánh sáng94
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.