Thực thi tập lệnh bash theo nghĩa đen sau mỗi 3 ngày


8

Tôi muốn thực thi một kịch bản shell theo nghĩa đen sau mỗi 3 ngày. Sử dụng crontab 01 00 */3 * *sẽ không thực sự đáp ứng điều kiện, bởi vì nó sẽ chạy vào ngày 31 và sau đó lại vào ngày đầu tiên của tháng. Các */3cú pháp cũng giống như nói 1,4,7 ... 25,28,31.

Cần có cách để tự tạo kịch bản kiểm tra các điều kiện và thoát nếu 3 ngày không trôi qua. Vì vậy, crontab thực thi kịch bản hàng ngày, nhưng chính kịch bản sẽ kiểm tra xem đã 3 ngày trôi qua.

Tôi thậm chí đã tìm thấy một số mã, nhưng nó đã cho tôi lỗi cú pháp, bất kỳ trợ giúp sẽ được đánh giá cao.

if (! (date("z") % 3)) {
     exit;
}

main.sh: line 1: syntax error near unexpected token `"z"'
main.sh: line 1: `if (! (date("z") % 3)) {'

4
Bạn có ý nghĩa gì chính xác? Làm thế nào */3không hoạt động? "nếu 3 ngày không trôi qua": ba ngày kể từ ngày gì? Vui lòng chỉnh sửa câu hỏi của bạn và làm rõ.
terdon

4
nghĩa đen theo nghĩa đen? Đây có vẻ như là một câu hỏi x / y và bạn có thể thực sự muốn nói về những gì bạn đang cố gắng làm. Bạn đang cố gắng ngăn crontab chạy tập lệnh?
Journeyman Geek

1
Chỉnh sửa câu hỏi để giải thích tại sao giải pháp crontab không hoạt động.
Taavi

Một cái gì đó giống như date("z") % 3 == 0sẽ gặp phải một vấn đề tương tự: điều kiện sẽ là sai trong bốn ngày từ 29 tháng 12 đến 3 tháng 1, trừ khi tháng 12 đó là một phần của năm nhuận.
Rhymoid

Câu trả lời:


12

Để hủy bỏ ngay lập tức và thoát khỏi tập lệnh nếu lần thực hiện cuối cùng chưa có ít nhất một thời gian cụ thể trước đây, bạn có thể sử dụng phương pháp này yêu cầu một tệp bên ngoài lưu trữ ngày và giờ thực hiện cuối cùng.

Thêm các dòng này vào đầu tập lệnh Bash của bạn:

#!/bin/bash

# File that stores the last execution date in plain text:
datefile=/path/to/your/datefile

# Minimum delay between two script executions, in seconds. 
seconds=$((60*60*24*3))

# Test if datefile exists and compare the difference between the stored date 
# and now with the given minimum delay in seconds. 
# Exit with error code 1 if the minimum delay is not exceeded yet.
if test -f "$datefile" ; then
    if test "$(($(date "+%s")-$(date -f "$datefile" "+%s")))" -lt "$seconds" ; then
        echo "This script may not yet be started again."
        exit 1
    fi
fi

# Store the current date and time in datefile
date -R > "$datefile"

# Insert your normal script here:

Đừng quên đặt một giá trị có ý nghĩa datefile=và điều chỉnh giá trị seconds=theo nhu cầu của bạn ( $((60*60*24*3))ước tính thành 3 ngày).


Nếu bạn không muốn một tệp riêng biệt, bạn cũng có thể lưu trữ thời gian thực hiện cuối cùng trong dấu thời gian sửa đổi của tập lệnh của mình. Tuy nhiên, điều đó có nghĩa là thực hiện bất kỳ thay đổi nào đối với tệp tập lệnh của bạn sẽ đặt lại bộ đếm 3 và được xử lý như nếu tập lệnh đang chạy thành công.

Để thực hiện điều đó, hãy thêm đoạn mã bên dưới vào đầu tệp tập lệnh của bạn:

#!/bin/bash

# Minimum delay between two script executions, in seconds. 
seconds=$((60*60*24*3))

# Compare the difference between this script's modification time stamp 
# and the current date with the given minimum delay in seconds. 
# Exit with error code 1 if the minimum delay is not exceeded yet.
if test "$(($(date "+%s")-$(date -r "$0" "+%s")))" -lt "$seconds" ; then
    echo "This script may not yet be started again."
    exit 1
fi

# Store the current date as modification time stamp of this script file
touch -m -- "$0"

# Insert your normal script here:

Một lần nữa, đừng quên điều chỉnh giá trị của seconds=nhu cầu của bạn ( $((60*60*24*3))ước tính đến 3 ngày).


Có, ghi lại lần gọi thành công cuối cùng của một chương trình cụ thể yêu cầu một số loại lưu trữ dữ liệu ngoài và hệ thống tệp là một lựa chọn rõ ràng cho điều đó.
Kilian Foth

Thậm chí không cần lưu trữ ngày - chỉ cần chạm vào tệp mỗi lần và kiểm tra dấu thời gian của nó.
djsmiley2kStaysInside 26/9/2016

@ djsmiley2k Vâng, bạn nói đúng. Nếu rủi ro sửa đổi tệp tập lệnh theo cách thủ công gây ra độ trễ để đặt lại là chấp nhận được, người ta cũng có thể sử dụng dấu thời gian sửa đổi. Tôi đã thêm nó vào câu trả lời của tôi.
Chỉ huy Byte

Điều này có thể được đánh golf một chút bằng cách lưu dấu thời gian hiện tại vào tệp (thay vì ngày có thể đọc được của con người), do đó bạn có thể có được thời gian trôi qua với $[ $(date +%s) - $(< lastrun) ]. Nếu tập lệnh được chạy từ cron mỗi ngày một lần, tôi có thể thêm một chút chùng vào khoảng thời gian cần thiết, để nếu việc thực thi tập lệnh bị chậm một vài giây, lần sau sẽ không bỏ qua cả ngày. Đó là kiểm tra mỗi ngày nếu 71 giờ đã qua oslt.
ilkkachu

Thuật sĩ này với datefile thực sự có hiệu quả, cảm ơn bạn rất nhiều!
Taavi

12

Cron thực sự là công cụ sai cho việc này. Có thực sự là một công cụ thường được sử dụng và underloved gọi tại mà làm việc sức. tại được thiết kế chủ yếu để sử dụng tương tác và tôi chắc chắn rằng ai đó sẽ tìm ra cách tốt hơn để làm điều này.

Trong trường hợp của tôi, tôi có đoạn script tôi đang chạy được liệt kê trong testjobs.txt và bao gồm một dòng đọc.

Ví dụ, tôi sẽ có cái này là testjobs.txt

echo "cat" >> foo.txt
date >> foo.txt
at now + 3 days < testjobs.txt

Tôi có hai lệnh vô tội, có thể là shellscripts của bạn. Tôi chạy echo để đảm bảo rằng tôi có đầu ra xác định và ngày để xác nhận lệnh chạy khi cần thiết. Khi chạy lệnh này, nó sẽ kết thúc bằng cách thêm một công việc mới vào trong 3 ngày. (Tôi đã thử nghiệm với một phút - hoạt động)

Tôi khá chắc chắn rằng tôi sẽ được gọi theo cách mà tôi đã lạm dụng, nhưng nó là một công cụ hữu ích để lên lịch cho một lệnh được chạy tại một thời điểm hoặc x ngày sau một lệnh trước đó.


3
+1 atdường như là cách tiếp cận tốt hơn ở đây. Vấn đề với cách tiếp cận này là bất cứ khi nào bạn tự chạy tập lệnh, bạn sẽ thêm một tập hợp các attác vụ 3 ngày một lần .
Dewi Morgan

1
+1, nhưng một vấn đề khác (ngoài vấn đề được chỉ ra bởi @DewiMorgan) là nếu một tập lệnh bị lỗi, tất cả các tập lệnh tiếp theo sẽ không được khởi chạy (nếu tại sau điểm thất bại), cho đến khi một người nhận ra rằng tập lệnh đã có thất bại và khởi chạy lại nó Điều này có thể xấu, hoặc đôi khi tốt (ví dụ: thất bại vì một điều kiện không còn nữa: thật tốt khi nó không thử lại sau 3 ngày?). Và có một sự trôi dạt nhẹ mỗi lần (vài mili giây nếu atở trên cùng, hoặc có khả năng nhiều hơn nếu atở gần cuối tập lệnh thực thi dài)
Olivier Dulac

Tại có thể gửi email .... Đó có thể là một giải pháp cho thất bại. atq và atrm sẽ cho phép loại bỏ các công việc sai lầm có thể? Về lý thuyết, bạn có thể viết kịch bản một ngày cụ thể tại nhưng điều đó dường như không phù hợp và nắm giữ.
Journeyman Geek

Vì vậy, có một cronjob kiểm tra sự tồn tại của lần chạy tiếp theo tại; nếu không có, thêm một trong 3 ngày và cảnh báo (hoặc chạy ngay lập tức và kiểm tra lại).
djsmiley2kStaysInside 26/9/2016

3

Đầu tiên, đoạn mã ở trên là cú pháp Bash không hợp lệ, trông giống như Perl. Thứ hai, ztham số để datelàm cho nó xuất ra múi giờ số. +%jlà số ngày. Bạn cần:

if [[ ! $(( $(date +%j) % 3 )) ]] ;then
     exit
fi

Nhưng bạn vẫn sẽ thấy sự kỳ lạ vào cuối năm:

$ for i in 364 365  1 ; do echo "Day $i, $(( $i % 3 ))"; done
Day 364, 1
Day 365, 2
Day 1, 1

Bạn có thể có may mắn hơn với việc giữ số đếm trong một tệp và kiểm tra / cập nhật.


1
Perl không có date()hàm dựng sẵn, nhưng trông hơi giống ngày () trong PHP (trong đó zlà "Ngày trong năm (bắt đầu từ 0)")
ilkkachu

2

Nếu bạn có thể để tập lệnh chạy liên tục, bạn có thể làm:

while true; do

[inert code here]

sleep 259200
done

Vòng lặp này luôn luôn đúng, vì vậy nó sẽ luôn thực thi mã, sau đó đợi ba ngày trước khi bắt đầu lại vòng lặp.


Tại sao không while true?
Jonathan Leffler

Hừ! Nắm bắt tốt. Tôi đã không cần sử dụng nó trong một thời gian dài, tôi đã quên nó tồn tại lol
mkingsbu

2
Như at giải pháp, điều này sẽ trôi theo thời gian thực hiện của tập lệnh mỗi lần chạy. Tất nhiên điều đó có thể được giải quyết bằng cách tiết kiệm thời gian khi kịch bản bắt đầu và ngủ cho đến 3 ngày kể từ đó.
ilkkachu

2

Bạn có thể sử dụng anacron thay vì cron, nó được thiết kế chính xác để làm những gì bạn cần. Từ trang hướng dẫn:

Anacron có thể được sử dụng để thực hiện các lệnh theo định kỳ, với tần suất được chỉ định theo ngày. Không giống như cron (8), nó không cho rằng máy đang chạy liên tục. Do đó, nó có thể được sử dụng trên các máy không chạy 24 giờ mỗi ngày, để kiểm soát các công việc hàng ngày, hàng tuần và hàng tháng thường được kiểm soát bởi cron.

Khi được thực thi, Anacron đọc danh sách các công việc từ tệp cấu hình, thường là / etc / anacrontab (xem anacrontab (5)). Tập tin này chứa danh sách các công việc mà Anacron kiểm soát. Mỗi mục công việc chỉ định một khoảng thời gian tính bằng ngày, độ trễ tính bằng phút, mã định danh công việc duy nhất và lệnh shell.

Đối với mỗi công việc, Anacron kiểm tra xem công việc này đã được thực hiện trong n ngày qua chưa, trong đó n là khoảng thời gian được chỉ định cho công việc đó. Nếu không, Anacron chạy lệnh shell của công việc, sau khi chờ số phút được chỉ định làm tham số độ trễ.

Sau khi thoát lệnh, Anacron ghi lại ngày tháng trong tệp dấu thời gian đặc biệt cho công việc đó, để nó có thể biết khi nào thực hiện lại. Chỉ ngày được sử dụng để tính toán thời gian. Giờ không được sử dụng.


Đẹp, tôi chắc chắn sẽ kiểm tra Anacron. Tự hỏi tại sao nó lại bị đánh giá thấp như vậy nếu nó có thể làm được điều kỳ diệu như vậy
Taavi

Câu hỏi thực sự: tại sao bây giờ cron không được thay thế bằng một cái gì đó tốt hơn? fcron tồn tại và tôi nghĩ systemd đang làm việc theo giải pháp riêng của mình, nhưng tôi không cập nhật với tình trạng hiện tại.
Twinkles
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.