Làm cách nào để tạo một tập lệnh tùy ý trong unix?


93

Tôi muốn một daemonizer có thể biến một tập lệnh hoặc lệnh chung, tùy ý thành một daemon .

Có hai trường hợp phổ biến mà tôi muốn giải quyết:

  1. Tôi có một tập lệnh sẽ chạy mãi mãi. Nếu nó bị chết (hoặc khi khởi động lại), hãy khởi động lại nó. Đừng để có hai bản sao chạy cùng một lúc (phát hiện xem một bản sao đã chạy hay chưa và không khởi chạy nó trong trường hợp đó).

  2. Tôi có một tập lệnh đơn giản hoặc lệnh dòng lệnh mà tôi muốn tiếp tục thực thi lặp đi lặp lại mãi mãi (với một khoảng dừng ngắn giữa các lần chạy). Một lần nữa, không cho phép hai bản sao của tập lệnh chạy cùng một lúc.

Tất nhiên, việc viết một vòng lặp "while (true)" xung quanh tập lệnh trong trường hợp 2 và sau đó áp dụng giải pháp cho trường hợp 1 là rất nhỏ, nhưng một giải pháp chung hơn sẽ chỉ giải quyết trực tiếp trường hợp 2 vì điều đó áp dụng cho tập lệnh trong trường hợp 1 như tốt (bạn có thể chỉ muốn tạm dừng ngắn hơn hoặc không tạm dừng nếu kịch bản không có ý định chết (tất nhiên nếu kịch bản thực sự không bao giờ chết thì tạm dừng thực sự không quan trọng)).

Lưu ý rằng giải pháp không nên liên quan đến việc thêm mã khóa tệp hoặc ghi PID vào các tập lệnh hiện có.

Cụ thể hơn, tôi muốn một chương trình "daemonize" mà tôi có thể chạy

% daemonize myscript arg1 arg2

hoặc, ví dụ,

% daemonize 'echo `date` >> /tmp/times.txt'

điều này sẽ giữ cho danh sách ngày càng tăng được thêm vào times.txt. (Lưu ý rằng nếu (các) đối số để daemonize là một tập lệnh chạy mãi mãi như trong trường hợp 1 ở trên, thì daemonize sẽ vẫn làm đúng, khởi động lại nó khi cần thiết.) Sau đó, tôi có thể đặt một lệnh như trên trong .login của mình và / hoặc di chuyển nó hàng giờ hoặc hàng phút (tùy thuộc vào mức độ lo lắng của tôi về việc nó chết bất ngờ).

NB: Tập lệnh daemonize sẽ cần nhớ chuỗi lệnh mà nó đang daemonizing để nếu chuỗi lệnh tương tự được daemonize một lần nữa, nó sẽ không khởi chạy bản sao thứ hai.

Ngoài ra, giải pháp lý tưởng nên hoạt động trên cả OS X và linux nhưng các giải pháp cho cái này hay cái khác đều được hoan nghênh.

CHỈNH SỬA: Sẽ ổn nếu bạn phải gọi nó bằng sudo daemonize myscript myargs.

(Nếu tôi nghĩ về điều này hoàn toàn sai hoặc có những giải pháp từng phần nhanh chóng và bẩn thỉu, tôi cũng muốn nghe điều đó.)


Tái bút: Trong trường hợp nó hữu ích, đây là một câu hỏi tương tự dành riêng cho python.

này câu trả lời cho một câu hỏi tương tự có những gì dường như là một thành ngữ hữu ích cho một demonizing nhanh chóng-và-bẩn của một kịch bản tùy ý:


1
Xem serverfault.com/questions/311593/… để biết phiên bản shell thuần
w00t

Câu trả lời:


90

Bạn có thể daemonize hóa bất kỳ tệp thực thi nào trong Unix bằng cách sử dụng nohup và toán tử &:

nohup yourScript.sh script args&

Lệnh nohup cho phép bạn tắt phiên trình bao của mình mà không làm chết tập lệnh của bạn, trong khi & đặt tập lệnh của bạn ở chế độ nền để bạn nhận được lời nhắc trình bao để tiếp tục phiên của mình. Vấn đề nhỏ duy nhất với điều này là lỗi chuẩn và lỗi chuẩn đều được gửi đến ./nohup.out, vì vậy nếu bạn bắt đầu một số tập lệnh trong trang viên này, đầu ra của chúng sẽ bị đan xen. Một lệnh tốt hơn sẽ là:

nohup yourScript.sh script args >script.out 2>script.error&

Điều này sẽ gửi tiêu chuẩn ra tệp bạn chọn và lỗi tiêu chuẩn đến tệp khác mà bạn chọn. Nếu bạn chỉ muốn sử dụng một tệp cho cả lỗi chuẩn và lỗi chuẩn, bạn có thể cho chúng tôi điều này:

nohup yourScript.sh script args >script.out 2>&1 &

2> & 1 ra lệnh cho trình bao chuyển hướng lỗi chuẩn (bộ mô tả tệp 2) đến cùng một tệp như chuẩn ra (bộ mô tả tệp 1).

Để chạy một lệnh chỉ một lần và khởi động lại nếu lệnh chết, bạn có thể sử dụng tập lệnh này:

#!/bin/bash

if [[ $# < 1 ]]; then
    echo "Name of pid file not given."
    exit
fi

# Get the pid file's name.
PIDFILE=$1
shift

if [[ $# < 1 ]]; then
    echo "No command given."
    exit
fi

echo "Checking pid in file $PIDFILE."

#Check to see if process running.
PID=$(cat $PIDFILE 2>/dev/null)
if [[ $? = 0 ]]; then
    ps -p $PID >/dev/null 2>&1
    if [[ $? = 0 ]]; then
        echo "Command $1 already running."
        exit
    fi
fi

# Write our pid to file.
echo $$ >$PIDFILE

# Get command.
COMMAND=$1
shift

# Run command until we're killed.
while true; do
    $COMMAND "$@"
    sleep 10 # if command dies immediately, don't go into un-ctrl-c-able loop
done

Đối số đầu tiên là tên của tệp pid sẽ sử dụng. Đối số thứ hai là lệnh. Và tất cả các đối số khác là đối số của lệnh.

Nếu bạn đặt tên cho script này là restart.sh thì đây là cách bạn sẽ gọi nó:

nohup restart.sh pidFileName yourScript.sh script args >script.out 2>&1 &

Tuyệt vời; cảm ơn bạn. Tôi tự hỏi nếu nó cũng nên có một tùy chọn cho sự chậm trễ khi khởi động lại. Hoặc có thể tốt hơn chỉ sử dụng nó kết hợp với điều này: stackoverflow.com/questions/555116/…
dreeves

4
Điều này chỉ xử lý SIGHUP, có các tín hiệu nguy hiểm (thông thường) khác cần được xử lý.
Tim Post

Một cách khác để cải thiện tập lệnh này có lẽ là làm cho nó có một vị trí tốt để đặt $ PIDFILE của riêng nó thay vì yêu cầu nó phải được chỉ định như một đối số. Nó thậm chí không làm sạch sau khi chính nó! (mà nên thẳng thắn với một trap EXIT)
Steven Lu

Ngoài ra, hãy xem xét rằng việc sử dụng <trong testlà so sánh ASCII không phải là so sánh số nguyên. Nó có thể vẫn hoạt động, nhưng có thể dẫn đến lỗi.
Steven Lu

Tôi đã đăng các bản sửa lỗi của mình cho tập lệnh này ở đây .
Steven Lu

34

Tôi xin lỗi vì câu trả lời dài dòng (vui lòng xem nhận xét về cách câu trả lời của tôi xác định thông số kỹ thuật). Tôi đang cố gắng trở nên toàn diện, vì vậy bạn càng có cơ hội tốt nhất có thể. :-)

Nếu bạn có thể cài đặt các chương trình (có quyền truy cập root) và sẵn sàng thực hiện công việc một lần để thiết lập tập lệnh của bạn để thực thi daemon (tức là, liên quan nhiều hơn việc chỉ định các đối số dòng lệnh để chạy trên dòng lệnh, nhưng chỉ cần thực hiện một lần cho mỗi dịch vụ), tôi có một cách mạnh mẽ hơn.

Nó liên quan đến việc sử dụng daemontools . Phần còn lại của bài đăng mô tả cách thiết lập dịch vụ bằng daemontools.

Thiết lập ban đầu

  1. Làm theo hướng dẫn trong Cách cài đặt daemontools . Một số bản phân phối (ví dụ: Debian, Ubuntu) đã có các gói cho nó, vì vậy chỉ cần sử dụng nó.
  2. Tạo một thư mục có tên /service. Trình cài đặt lẽ ra đã thực hiện việc này, nhưng chỉ cần xác minh hoặc nếu cài đặt theo cách thủ công. Nếu bạn không thích vị trí này, bạn có thể thay đổi nó trong svscanboottập lệnh của mình , mặc dù hầu hết người dùng daemontools đã quen với việc sử dụng /servicevà sẽ bối rối nếu bạn không sử dụng nó.
  3. Nếu bạn đang sử dụng Ubuntu hoặc một bản phân phối khác không sử dụng tiêu chuẩn init(tức là không sử dụng /etc/inittab), bạn sẽ cần sử dụng bản cài đặt sẵn inittablàm cơ sở để sắp xếp svscanbootđược gọi bởi init. Nó không khó, nhưng bạn cần biết cách cấu hình inithệ điều hành của bạn sử dụng. svscanbootlà một tập lệnh gọi svscan, thực hiện công việc chính là tìm kiếm dịch vụ; nó được gọi từ initđó initsẽ sắp xếp để khởi động lại nếu nó chết vì bất kỳ lý do gì.

Thiết lập theo từng dịch vụ

  1. Mỗi dịch vụ cần một danh mục dịch vụ , nơi lưu trữ thông tin quản lý về dịch vụ. Bạn cũng có thể tạo một vị trí để chứa các thư mục dịch vụ này để chúng ở cùng một nơi; thường thì tôi sử dụng /var/lib/svscan, nhưng bất kỳ vị trí mới nào cũng được.
  2. Tôi thường sử dụng một tập lệnh để thiết lập thư mục dịch vụ, để tiết kiệm nhiều công việc lặp đi lặp lại thủ công. ví dụ,

    sudo mkservice -d /var/lib/svscan/some-service-name -l -u user -L loguser "command line here"

    đâu some-service-namelà tên bạn muốn đặt cho dịch vụ của mình, userlà người dùng để chạy dịch vụ đó với tư cách loguserlà người dùng chạy trình ghi nhật ký với tư cách là. (Ghi nhật ký được giải thích một chút.)

  3. Dịch vụ của bạn phải chạy ở phía trước . Nếu nền chương trình của bạn theo mặc định, nhưng có tùy chọn để tắt, hãy làm như vậy. Nếu chương trình của bạn chạy nền mà không có cách nào để tắt nó, hãy đọc tiếp fghack, mặc dù điều này phải đánh đổi: bạn không còn có thể kiểm soát chương trình bằng cách sử dụng svc.
  4. Chỉnh sửa runtập lệnh để đảm bảo tập lệnh làm những gì bạn muốn. Bạn có thể cần thực hiện sleepcuộc gọi ở trên cùng, nếu bạn muốn dịch vụ của mình thường xuyên thoát.
  5. Khi mọi thứ được thiết lập đúng, hãy tạo một liên kết tượng trưng /serviceđể trỏ đến thư mục dịch vụ của bạn. (Không đặt các thư mục dịch vụ trực tiếp bên trong /service; việc xóa dịch vụ khỏi svscanđồng hồ của sẽ khó hơn .)

Ghi nhật ký

  1. Cách ghi nhật ký của daemontools là yêu cầu dịch vụ ghi thông báo nhật ký vào đầu ra chuẩn (hoặc lỗi chuẩn, nếu bạn đang sử dụng các tập lệnh được tạo bằng mkservice); svscanđảm nhận việc gửi thông báo nhật ký đến dịch vụ ghi nhật ký.
  2. Dịch vụ ghi nhật ký lấy các thông báo nhật ký từ đầu vào chuẩn. Tập lệnh dịch vụ ghi nhật ký được tạo bởi mkservicesẽ tạo các tệp nhật ký tự động xoay vòng, có dấu thời gian trong log/mainthư mục. Tệp nhật ký hiện tại được gọi current.
  3. Dịch vụ ghi nhật ký có thể được bắt đầu và dừng độc lập với dịch vụ chính.
  4. Đưa các tệp nhật ký qua tai64nlocalsẽ dịch các dấu thời gian sang định dạng con người có thể đọc được. (TAI64N là dấu thời gian nguyên tử 64 bit với số nano giây.)

Kiểm soát dịch vụ

  1. Sử dụng svstatđể nhận trạng thái của một dịch vụ. Lưu ý rằng dịch vụ ghi nhật ký là độc lập và có trạng thái riêng.
  2. Bạn kiểm soát dịch vụ của mình (bắt đầu, dừng, khởi động lại, v.v.) bằng cách sử dụng svc. Ví dụ, để khởi động lại dịch vụ của bạn, hãy sử dụng svc -t /service/some-service-name; -tnghĩa là "gửi SIGTERM".
  3. Các tín hiệu khác có sẵn bao gồm -h( SIGHUP), -a( SIGALRM), -1( SIGUSR1), -2( SIGUSR2) và -k( SIGKILL).
  4. Để xuống dịch vụ, hãy sử dụng -d. Bạn cũng có thể ngăn một dịch vụ tự động khởi động khi khởi động bằng cách tạo một tệp có tên downtrong thư mục dịch vụ.
  5. Để bắt đầu dịch vụ, hãy sử dụng -u. Điều này là không cần thiết trừ khi bạn đã hạ xuống nó trước đó (hoặc thiết lập nó không tự động khởi động).
  6. Để yêu cầu người giám sát thoát ra, sử dụng -x; cũng thường được sử dụng với -dđể chấm dứt dịch vụ. Đây là cách thông thường để cho phép xóa một dịch vụ, nhưng bạn phải hủy liên kết dịch vụ đó từ /servicetrước, nếu không svscansẽ khởi động lại trình giám sát. Ngoài ra, nếu bạn đã tạo dịch vụ của mình bằng dịch vụ ghi nhật ký ( mkservice -l), hãy nhớ thoát khỏi trình giám sát ghi nhật ký (ví dụ svc -dx /var/lib/svscan/some-service-name/log:) trước khi xóa thư mục dịch vụ.

Tóm lược

Ưu điểm:

  1. daemontools cung cấp một cách chắc chắn để tạo và quản lý dịch vụ. Tôi sử dụng nó cho các máy chủ của mình và tôi thực sự khuyên bạn nên sử dụng nó.
  2. Hệ thống ghi nhật ký của nó rất mạnh mẽ, cũng như cơ sở tự động khởi động lại dịch vụ.
  3. Bởi vì nó khởi động các dịch vụ bằng tập lệnh shell mà bạn viết / điều chỉnh, bạn có thể điều chỉnh dịch vụ của mình theo cách bạn muốn.
  4. Công cụ kiểm soát dịch vụ mạnh mẽ: bạn có thể gửi hầu hết mọi tín hiệu đến một dịch vụ và có thể đưa dịch vụ lên và xuống một cách đáng tin cậy.
  5. Các dịch vụ của bạn được đảm bảo một môi trường thực thi sạch: chúng sẽ thực thi với cùng một môi trường, giới hạn quy trình, v.v., như những gì initcung cấp.

Nhược điểm:

  1. Mỗi dịch vụ cần một chút thiết lập. Rất may, điều này chỉ cần thực hiện một lần cho mỗi dịch vụ.
  2. Các dịch vụ phải được thiết lập để chạy ở nền trước. Ngoài ra, để có kết quả tốt nhất, chúng nên được thiết lập để ghi vào đầu ra chuẩn / lỗi chuẩn, thay vì nhật ký hệ thống hoặc các tệp khác.
  3. Đường cong học tập mạnh mẽ nếu bạn chưa quen với cách hoạt động của daemontools. Bạn phải khởi động lại các dịch vụ bằng cách sử dụng svcvà không thể chạy trực tiếp các tập lệnh đang chạy (vì khi đó chúng sẽ không nằm dưới sự kiểm soát của người giám sát).
  4. Rất nhiều hồ sơ quản lý và rất nhiều quy trình vệ sinh. Mỗi dịch vụ cần thư mục dịch vụ riêng và mỗi dịch vụ sử dụng một quy trình giám sát để tự động khởi động lại dịch vụ nếu nó chết. (Nếu bạn có nhiều dịch vụ, bạn sẽ thấy rất nhiều các supervisequá trình trong bảng quá trình của bạn.)

Cân bằng lại, tôi nghĩ daemontools là một hệ thống tuyệt vời cho nhu cầu của bạn. Tôi hoan nghênh bất kỳ câu hỏi nào về cách thiết lập và duy trì nó.


Câu trả lời của tôi xác định thông số kỹ thuật như thế nào: 1. Bạn phải thiết lập các dịch vụ, miễn là bạn không thiết lập các bản sao (và miễn là dịch vụ của bạn không tự nền), sẽ không có bản sao nào xảy ra. 2. supervise, người giám sát, đảm nhận việc khởi động lại bất kỳ dịch vụ nào thoát ra. Nó đợi một giây giữa các lần nghỉ; nếu đó là không đủ thời gian cho bạn, hãy đặt ở đầu tập lệnh chạy dịch vụ.
Chris Jester-Young

2a. superviseđược hỗ trợ bởi chính nó svscan, vì vậy nếu người giám sát chết, nó sẽ được khởi động lại. 2b. svscanđược hỗ trợ bởi init, sẽ tự động khởi động lại svscannếu cần. 2c. Nếu bạn initchết vì bất kỳ lý do gì, thì bạn vẫn đang gặp rắc rối. :-P
Chris Jester-Young

Để trả lời các câu hỏi khác về quản lý nhà, hệ thống daemontools không sử dụng tệp PID, vì chúng có thể bị cũ. Thay vào đó, tất cả thông tin quy trình được lưu giữ bởi người giám sát hỗ trợ một dịch vụ nhất định. Người giám sát duy trì một loạt các tệp (và FIFO) trong thư mục dịch vụ mà các công cụ thích svstatsvccó thể làm việc với.
Chris Jester-Young

3
Chúng ta nên có nhiều bài viết như thế này trên SO và trên mạng nói chung. Không chỉ là một công thức làm thế nào để đạt được hiệu quả mong muốn mà còn là một công thức khó giải thích. Tại sao tôi không thể ủng hộ điều này nhiều hơn một lần? : |
skytreader

12

Tôi nghĩ bạn có thể muốn thử start-stop-daemon(8). Kiểm tra các tập lệnh trong /etc/init.dbất kỳ bản phân phối Linux nào để biết ví dụ. Nó có thể tìm các quy trình đã bắt đầu bằng dòng lệnh được gọi hoặc tệp PID, vì vậy nó phù hợp với tất cả các yêu cầu của bạn ngoại trừ việc trở thành cơ quan giám sát cho tập lệnh của bạn. Nhưng bạn luôn có thể bắt đầu một tập lệnh cơ quan giám sát daemon khác chỉ khởi động lại tập lệnh của bạn nếu cần.


Không có start-stop-daemon trong Fedora, vì vậy các tập lệnh phụ thuộc vào nó không thể di động. Xem: fedoraproject.org/wiki/Features/start-stop-daemon
Bengt

Chỉ là một thông báo cho người dùng OSX: không start-stop-daemoncó cả (kể từ ngày 10.9).
mklement0

@ mklement0 Chà ... rất nhiều thứ đã thay đổi trong gần 5 năm.
Alex B

Của tôi, làm sao thời gian trôi. start-stop-daemonTuy nhiên, vẫn còn tồn tại và phát triển trên Linux; nhưng sau khi đọc câu trả lời stackoverflow.com/a/525406/45375 tôi nhận ra rằng OSX làm việc của riêng của nó: launchd.
mklement0

12

Bạn nên xem xét daemonize . Nó cho phép phát hiện bản sao thứ hai (nhưng nó sử dụng cơ chế khóa tệp). Ngoài ra, nó hoạt động trên các bản phân phối UNIX và Linux khác nhau.

Nếu bạn cần tự động khởi động ứng dụng của mình dưới dạng daemon, thì bạn cần tạo init-script thích hợp.

Bạn có thể sử dụng mẫu sau:

#!/bin/sh
#
# mydaemon     This shell script takes care of starting and stopping
#               the <mydaemon>
#

# Source function library
. /etc/rc.d/init.d/functions


# Do preliminary checks here, if any
#### START of preliminary checks #########


##### END of preliminary checks #######


# Handle manual control parameters like start, stop, status, restart, etc.

case "$1" in
  start)
    # Start daemons.

    echo -n $"Starting <mydaemon> daemon: "
    echo
    daemon <mydaemon>
    echo
    ;;

  stop)
    # Stop daemons.
    echo -n $"Shutting down <mydaemon>: "
    killproc <mydaemon>
    echo

    # Do clean-up works here like removing pid files from /var/run, etc.
    ;;
  status)
    status <mydaemon>

    ;;
  restart)
    $0 stop
    $0 start
    ;;

  *)
    echo $"Usage: $0 {start|stop|status|restart}"
    exit 1
esac

exit 0

1
Có vẻ như một ứng cử viên cho câu trả lời chính xác. Đặc biệt là xem xét "kiểm tra cá thể duy nhất" của nó.
Martin Wickman

Đây có thể là câu trả lời tốt nhất có thể - tôi không chắc - nhưng nếu bạn nghĩ là như vậy, bạn cũng có thể đưa ra lời giải thích cho lý do tại sao thông số tôi đưa ra trong câu hỏi bị nhầm lẫn không?
dreeves

Tôi không thích killprocở phần dừng: nếu bạn có một quy trình, chẳng hạn như đã chạy java, điều đó killprocsẽ khiến tất cả các quy trình Java khác cũng bị giết.
Chris Jester-Young

1
Từ /etc/rc.d/init.d/functions, daemonize chỉ bắt đầu nhị phân từ một shell mới: $ cgroup $ nice / bin / bash -c $corelimit >/dev/null 2>&1 ; $*Vì vậy, tôi nghi ngờ nó sẽ daemonize hóa bất cứ thứ gì ...
mbonnin

1
Tôi biết điều này là cũ, nhưng đối với bất kỳ ai tìm thấy điều này sau này ... điều này là chính xác. "daemon" như được định nghĩa trong /etc/init.d/functions không thực sự tạo daemonize cho bạn. Nó chỉ là một trình bao bọc để thực hiện các nhóm, kiểm tra xem quy trình đã chạy chưa, đặt người dùng, đặt các giá trị đẹp và ulimit, v.v. Nó không đa dạng hóa quy trình cho bạn. Đó vẫn là công việc của riêng bạn. :)
jakem

7

Để thay thế cho lệnh đã được đề cập daemonizedaemontools, có lệnh daemon của gói libslack.

daemon khá dễ cấu hình và quan tâm đến tất cả những thứ tẻ nhạt của daemon như tự động khởi động lại, ghi nhật ký hoặc xử lý pidfile.


5

Nếu bạn đang sử dụng OS X cụ thể, tôi khuyên bạn nên xem cách khởi động hoạt động. Nó sẽ tự động kiểm tra để đảm bảo tập lệnh của bạn đang chạy và khởi chạy lại nếu cần. Nó cũng bao gồm tất cả các loại tính năng lập lịch, v.v. Nó phải đáp ứng cả yêu cầu 1 và 2.

Để đảm bảo chỉ một bản sao của tập lệnh của bạn có thể chạy, bạn cần sử dụng tệp PID. Nói chung, tôi ghi một tệp vào /var/run/.pid có chứa PID của phiên bản đang chạy hiện tại. nếu tệp tồn tại khi chương trình chạy, nó sẽ kiểm tra xem PID trong tệp có thực sự đang chạy hay không (chương trình có thể đã bị lỗi hoặc quên xóa tệp PID). Nếu có, hãy hủy bỏ. Nếu không, hãy bắt đầu chạy và ghi đè lên tệp PID.


5

Daemontools ( http://cr.yp.to/daemontools.html ) là một tập hợp các tiện ích khá cứng dùng để làm việc này, được viết bởi dj bernstein. Tôi đã sử dụng điều này với một số thành công. Phần khó chịu về nó là không có tập lệnh nào trả lại bất kỳ kết quả hiển thị nào khi bạn chạy chúng - chỉ là những mã trả về vô hình. Nhưng một khi nó đang chạy, nó có khả năng chống đạn.


Có, tôi cũng sẽ viết một mục sử dụng daemontools. Tôi sẽ viết bài đăng của riêng mình, vì tôi hy vọng câu trả lời của mình sẽ toàn diện hơn nhiều và hy vọng sẽ nhận được tiền thưởng theo cách đó. Chúng ta sẽ thấy. :-)
Chris Jester-Young

3

Đầu tiên, truy cập createDaemon()từ http://code.activestate.com/recipes/278731/

Sau đó, mã chính:

import subprocess
import sys
import time

createDaemon()

while True:
    subprocess.call(" ".join(sys.argv[1:]),shell=True)
    time.sleep(10)

Ồ, cảm ơn! Muốn làm cho nó tổng quát hơn một chút để bạn có thể làm "daemonize foo arg1 arg2" cũng như "daemonize 'foo arg1 arg2'"?
dreeves

Ok, bây giờ nó sẽ nối các đối số - tuy nhiên bạn sẽ phải thay đổi nó nếu bạn muốn có khoảng trắng bên trong các đối số của mình.
Douglas Leeder

Cảm ơn Douglas! Tuy nhiên, có một lỗ hổng lớn: chạy "daemonize foo" hai lần sẽ bắt đầu chạy hai bản sao của foo.
dreeves

Bạn có thể thêm một số mã ghi PID, nhưng nó có thể là tốt nhất để chỉ chạy kịch bản một lần ...
Douglas Leeder

Tôi nghĩ điều đó là cơ bản cho toàn bộ khái niệm trình bao bọc "daemonize". (Ví dụ: sau đó tôi có thể chạy nó hàng giờ hoặc hàng phút để đảm bảo nó luôn chạy.) Tôi có nghĩ về điều này sai không? Liệu createDaemon đã đảm bảo điều đó bằng cách nào đó? Điều gì về sau khi khởi động lại?
cơn gió

1

Đây là phiên bản làm việc hoàn chỉnh với một ví dụ mà bạn có thể sao chép vào một thư mục trống và dùng thử (sau khi cài đặt các phần phụ thuộc CPAN, đó là Getopt :: Long , File :: Spec , File :: PidIPC :: System: : Đơn giản - tất cả đều khá chuẩn và rất được khuyến khích cho bất kỳ hacker nào: bạn có thể cài đặt tất cả chúng cùng một lúc với cpan <modulename> <modulename> ...).


keepAlive.pl:

#!/usr/bin/perl

# Usage:
# 1. put this in your crontab, to run every minute:
#     keepAlive.pl --pidfile=<pidfile> --command=<executable> <arguments>
# 2. put this code somewhere near the beginning of your script,
#    where $pidfile is the same value as used in the cron job above:
#     use File::Pid;
#     File::Pid->new({file => $pidfile})->write;

# if you want to stop your program from restarting, you must first disable the
# cron job, then manually stop your script. There is no need to clean up the
# pidfile; it will be cleaned up automatically when you next call
# keepAlive.pl.

use strict;
use warnings;

use Getopt::Long;
use File::Spec;
use File::Pid;
use IPC::System::Simple qw(system);

my ($pid_file, $command);
GetOptions("pidfile=s"   => \$pid_file,
           "command=s"   => \$command)
    or print "Usage: $0 --pidfile=<pidfile> --command=<executable> <arguments>\n", exit;

my @arguments = @ARGV;

# check if process is still running
my $pid_obj = File::Pid->new({file => $pid_file});

if ($pid_obj->running())
{
    # process is still running; nothing to do!
    exit 0;
}

# no? restart it
print "Pid " . $pid_obj->pid . " no longer running; restarting $command @arguments\n";

system($command, @arguments);

example.pl:

#!/usr/bin/perl

use strict;
use warnings;

use File::Pid;
File::Pid->new({file => "pidfile"})->write;

print "$0 got arguments: @ARGV\n";

Bây giờ bạn có thể gọi ví dụ trên với: ./keepAlive.pl --pidfile=pidfile --command=./example.pl 1 2 3và tệp pidfilesẽ được tạo, và bạn sẽ thấy đầu ra:

Pid <random number here> no longer running; restarting ./example.pl 1 2 3
./example.pl got arguments: 1 2 3

Tôi tin rằng điều này không hoàn toàn để thông số kỹ thuật, nếu tôi hiểu đúng. Trong giải pháp của bạn (cảm ơn, btw!), Chương trình bạn muốn daemonize phải được sửa đổi để ghi PID của nó vào tệp PID. Tôi hy vọng về một tiện ích có thể đa dạng hóa một tập lệnh tùy ý.
dreeves

@dreeves: vâng, nhưng có hai cách để giải quyết vấn đề đó: 1. tập lệnh được gọi bởi keepAlive.pl (ví dụ: example.pl) có thể đơn giản là một trình bao bọc để thực thi chương trình thực, hoặc 2. keepAlive.pl có thể phân tích cú pháp bảng của các quy trình hệ thống đang hoạt động (với Proc :: ProcessTable của CPAN) để cố gắng tìm quy trình liên quan và pid của nó).
Ether

1

Bạn cũng có thể thử Monit . Monit là một dịch vụ giám sát và báo cáo về các dịch vụ khác. Mặc dù nó chủ yếu được sử dụng như một cách để thông báo (qua email và sms) về các vấn đề thời gian chạy, nó cũng có thể thực hiện những gì mà hầu hết các đề xuất khác ở đây đã ủng hộ. Nó có thể tự động (lại) khởi động và dừng chương trình, gửi email, khởi tạo các tập lệnh khác và duy trì nhật ký đầu ra mà bạn có thể nhận. Ngoài ra, tôi thấy rất dễ cài đặt và bảo trì vì có tài liệu chắc chắn.


1

Bạn có thể thử để trở thành bất tử Nó là trình giám sát đa nền tảng * nix (OS bất khả tri).

Để thử nhanh trên macOS:

brew install immortal

Trong trường hợp bạn đang sử dụng FreeBSD từ các cổng hoặc bằng cách sử dụng pkg:

pkg install immortal

Đối với Linux bằng cách tải xuống các tệp nhị phân được biên dịch trước hoặc từ nguồn: https://immortal.run/source/

Bạn có thể sử dụng nó như thế này:

immortal -l /var/log/date.log date

Hoặc bằng tệp YAML cấu hình cung cấp cho bạn nhiều tùy chọn hơn, ví dụ:

cmd: date
log:
    file: /var/log/date.log
    age: 86400 # seconds
    num: 7     # int
    size: 1    # MegaBytes
    timestamp: true # will add timesamp to log

Nếu bạn muốn giữ lại đầu ra lỗi chuẩn trong một tệp riêng biệt, bạn có thể sử dụng một cái gì đó như:

cmd: date
log:
    file: /var/log/date.log
    age: 86400 # seconds
    num: 7     # int
    size: 1    # MegaBytes
stderr:
    file: /var/log/date-error.log
    age: 86400 # seconds
    num: 7     # int
    size: 1    # MegaBytes
    timestamp: true # will add timesamp to log

0

Tôi đã thực hiện một loạt các cải tiến trên câu trả lời khác .

  1. stdout ra khỏi tập lệnh này hoàn toàn được tạo thành từ stdout đến từ con của nó KHÔNG PHẢI nó thoát ra do phát hiện rằng lệnh đã được chạy
  2. dọn dẹp sau pidfile của nó khi chấm dứt
  3. khoảng thời gian chờ có thể định cấu hình tùy chọn (Chấp nhận bất kỳ đối số số dương nào, được gửi tới sleep)
  4. nhắc nhở sử dụng trên -h
  5. thực hiện lệnh tùy ý, thay vì thực hiện lệnh đơn lẻ. Đối số cuối cùng HOẶC args còn lại (nếu nhiều đối số cuối cùng) được gửi đến eval, vì vậy bạn có thể xây dựng bất kỳ loại tập lệnh shell nào dưới dạng chuỗi để gửi tới tập lệnh này dưới dạng đối số cuối cùng (hoặc args cuối) để nó đa dạng hóa
  6. so sánh số lượng đối số được thực hiện với -ltthay vì<

Đây là kịch bản:

#!/bin/sh

# this script builds a mini-daemon, which isn't a real daemon because it
# should die when the owning terminal dies, but what makes it useful is
# that it will restart the command given to it when it completes, with a
# configurable timeout period elapsing before doing so.

if [ "$1" = '-h' ]; then
    echo "timeout defaults to 1 sec.\nUsage: $(basename "$0") sentinel-pidfile [timeout] command [command arg [more command args...]]"
    exit
fi

if [ $# -lt 2 ]; then
    echo "No command given."
    exit
fi

PIDFILE=$1
shift

TIMEOUT=1
if [[ $1 =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
        TIMEOUT=$1
        [ $# -lt 2 ] && echo "No command given (timeout was given)." && exit
        shift
fi

echo "Checking pid in file ${PIDFILE}." >&2

#Check to see if process running.
if [ -f "$PIDFILE" ]; then
    PID=$(< $PIDFILE)
    if [ $? = 0 ]; then
        ps -p $PID >/dev/null 2>&1
        if [ $? = 0 ]; then
            echo "This script is (probably) already running as PID ${PID}."
            exit
        fi
    fi
fi

# Write our pid to file.
echo $$ >$PIDFILE

cleanup() {
        rm $PIDFILE
}
trap cleanup EXIT

# Run command until we're killed.
while true; do
    eval "$@"
    echo "I am $$ and my child has exited; restart in ${TIMEOUT}s" >&2
    sleep $TIMEOUT
done

Sử dụng:

$ term-daemonize.sh pidfilefortesting 0.5 'echo abcd | sed s/b/zzz/'
Checking pid in file pidfilefortesting.
azzzcd
I am 79281 and my child has exited; restart in 0.5s
azzzcd
I am 79281 and my child has exited; restart in 0.5s
azzzcd
I am 79281 and my child has exited; restart in 0.5s
^C

$ term-daemonize.sh pidfilefortesting 0.5 'echo abcd | sed s/b/zzz/' 2>/dev/null
azzzcd
azzzcd
azzzcd
^C

Hãy lưu ý rằng nếu bạn chạy tập lệnh này từ các thư mục khác nhau, nó có thể sử dụng các pidfiles khác nhau và không phát hiện bất kỳ trường hợp nào đang chạy hiện có. Vì nó được thiết kế để chạy và khởi động lại các lệnh tạm thời được cung cấp thông qua một đối số, không có cách nào để biết liệu một cái gì đó đã được bắt đầu hay chưa, bởi vì ai sẽ nói liệu đó có phải là lệnh giống nhau hay không? Để cải thiện việc thực thi chỉ chạy một trường hợp nào đó, cần phải có một giải pháp cụ thể cho tình huống đó.

Ngoài ra, để nó hoạt động như một daemon thích hợp, bạn phải sử dụng nohup (ở mức tối thiểu) như câu trả lời khác đã đề cập. Tôi đã không cố gắng cung cấp bất kỳ khả năng phục hồi nào đối với các tín hiệu mà quy trình có thể nhận được.

Một điểm nữa cần lưu ý là việc giết tập lệnh này (nếu nó được gọi từ một tập lệnh khác bị giết hoặc có tín hiệu) có thể không thành công trong việc giết đứa trẻ, đặc biệt nếu đứa trẻ là một tập lệnh khác . Tôi không chắc tại sao lại như vậy, nhưng nó có vẻ là một thứ gì đó liên quan đến cách thức evalhoạt động, điều này thật bí ẩn đối với tôi. Vì vậy, có thể thận trọng khi thay thế dòng đó bằng một thứ chỉ chấp nhận một lệnh duy nhất như trong câu trả lời kia.

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.