Làm thế nào (và lý do) để ngăn chặn một tập lệnh Bash khởi chạy lại quá sớm

Tác giả ChatGPT, T.Chín 15, 2024, 03:29:22 CHIỀU

« Chủ đề trước - Chủ đề tiếp »

0 Thành viên và 1 Khách đang xem chủ đề.

Đợi một chút.

Đôi khi, việc đảm bảo một tập lệnh shell Bash không chạy quá thường xuyên là rất hữu ích. Sau đây là một cách hay để đặt giới hạn thời gian phải hết hạn trước khi tập lệnh có thể chạy lại.


1. Hít thở một hơi

Tùy thuộc vào những gì nó đang làm và những tiến trình khác mà nó có thể khởi chạy, một tập lệnh Bash có thể tiêu tốn nhiều RAM và thời gian CPU như bất kỳ tiến trình ngốn tài nguyên nào khác. Có thể hữu ích khi giới hạn tần suất khởi chạy một tập lệnh như vậy.

Việc áp dụng một khoảng thời gian nghỉ giữa mỗi lần chạy một tập lệnh nặng sẽ ngăn không cho nó chiếm dụng tài nguyên. Các tập lệnh tốn kém về mặt tính toán có thể độc chiếm máy tính đến mức những người dùng khác gặp phải tình trạng giảm hiệu suất. Trong những trường hợp cực đoan, nếu tập lệnh gây ra nhiều sự cố đĩa, chẳng hạn, nó thậm chí có thể đẩy nhanh sự sụp đổ của phần cứng của bạn.

Tất nhiên, việc thiết kế một chương trình giới hạn thời gian bạn có thể khởi chạy lại một tập lệnh sẽ thêm mã vào tập lệnh của bạn và cung cấp cho nó thêm một việc để làm. Điều đó có vẻ phản tác dụng, nhưng nếu các kiểm tra nhẹ và nhanh, thì chi phí nhỏ của chúng sẽ được bù đắp bằng việc tiết kiệm tài nguyên.

2. Chiến lược dựa trên thời gian của chúng ta

Chúng tôi muốn áp dụng một khoảng thời gian tối thiểu phải hết hạn trước khi một tập lệnh có thể được lặp lại. Chúng tôi sẽ gọi khoảng thời gian này là tạm thời. Chúng tôi sẽ định nghĩa nó là khoảng thời gian giữa lần chạy trước kết thúc và lần chạy mới bắt đầu.

Chúng ta cần lưu thời gian mà tập lệnh hoàn tất, để lần sau khi tập lệnh được khởi chạy, chúng ta có thể truy xuất nó. Vì tập lệnh có thể dễ dàng xác định thời gian hiện tại, nên nó có thể tính toán sự khác biệt giữa thời gian khởi chạy và thời gian kết thúc của tập lệnh trước đó.

Nếu sự khác biệt đó nhỏ hơn mức trung gian có thể chấp nhận được thì tập lệnh sẽ thoát.

3. Thời gian trong Linux

Linux đã đếm giây kể từ kỷ nguyên Linux (thứ hai), diễn ra vào nửa đêm ngày 1 tháng 1 năm 1970, UTC. Chúng ta có thể xem thời gian và ngày tháng bằng cách sử dụng lệnh date.

Mã nguồn [Chọn]
date

Chúng ta có thể truyền các chỉ định định dạng cho date để có được đầu ra ở các kết xuất khác nhau. Để xem thời gian tính bằng giây kể từ kỷ nguyên, hãy sử dụng chữ thường 's':

Mã nguồn [Chọn]
date +%s

Có thể truy cập thời gian dưới dạng một số nguyên duy nhất giúp dễ dàng so sánh hai thời điểm và tìm ra khoảng thời gian giữa chúng. Điều đó hoàn hảo cho nhu cầu của chúng ta và chúng ta sẽ tận dụng tốt điều đó.

3. Lưu trữ và Truy xuất Thời gian

Chúng ta có thể dễ dàng ghi thời gian vào một tệp, chỉ bằng cách chuyển hướng đầu ra của lệnh date. Chúng ta có thể sử dụng cat để xác minh rằng nó hoạt động.

Mã nguồn [Chọn]
date +%s > temp.dat
cat temp.dat


Điều đó cho chúng ta một cách để lưu trữ dấu thời gian của mình. Lưu ý rằng chúng ta đang sử dụng một > duy nhất để chuyển hướng, do đó tệp được tạo lại mỗi lần và chỉ chứa một mục nhập duy nhất.

Bên trong tập lệnh của chúng ta, chúng ta sẽ cần mở tệp dấu thời gian và đọc giá trị đã lưu. Giá trị đó cần được giữ trong một biến để tập lệnh của chúng ta có thể sử dụng.

Nghe có vẻ khá phức tạp, nhưng có một mẹo đơn giản mà chúng ta có thể sử dụng. Bên trong tập lệnh của mình, chúng ta sẽ sử dụng lệnh source để đọc tệp dấu thời gian. Các lệnh bên trong tệp nguồn được thực thi như thể chúng là các lệnh bên trong tập lệnh của chúng ta.

Khi chúng ta lưu dấu thời gian, chúng ta thực sự sẽ lưu một lệnh tạo biến và gán giá trị thời gian cho biến đó. Khi tệp được lấy nguồn, tập lệnh của chúng ta sẽ thực thi lệnh đó, tạo biến và lưu trữ giá trị thời gian trong biến, vì vậy mọi thứ đã hoàn tất cho chúng ta.

Chúng ta có thể kiểm tra tiến trình đó trên dòng lệnh. Chúng ta soạn thảo và ghi lệnh vào một tệp. Lệnh tạo ra một biến có tên là previous_exit được đặt thành số giây kể từ thời điểm đó. Chúng ta lấy nguồn tệp. Sau đó, chúng ta kiểm tra xem biến có tên là previous_exit hiện có tồn tại không và xem giá trị mà nó giữ.

Mã nguồn [Chọn]
echo "previous_exit=$(date +%s)" > timestamp.log
source timestamp.log
echo $previous_exit


Nếu chúng ta kiểm tra nội dung của tệp, chúng ta có thể xác minh rằng giá trị mà biến lưu giữ chính là giá trị có trong tệp.

Mã nguồn [Chọn]
cat timestamp.log

Đó là một giải pháp hay và dễ dàng để lưu trữ và truy xuất giá trị thời gian.

5. Kết hợp tất cả lại với nhau

Chúng ta hãy cùng xem xét các yếu tố khác nhau của kịch bản.

Tập lệnh của tôi sẽ lưu trữ dấu thời gian trong một tệp có tên là .timestamp.log. Lưu ý ký tự đầu tiên là dấu chấm '.' có nghĩa là đó là tệp ẩn. Nó sẽ được lưu trữ trong thư mục gốc của tôi.

Tập lệnh tạo một biến có tên là timestamp_log để lưu trữ đường dẫn và tên tệp.

Tiếp theo, một hàm có tên là set_timestamp được định nghĩa. Khi hàm này được gọi, hàm này sẽ ghi giá trị được truyền vào nó vào tệp timestamp.log.

Mã nguồn [Chọn]
#!/bin/bash

# location of the timestamp log file
timestamp_log="/home/dave/.timestamp.log"

set_timestamp() {
  echo "previous_exit=$1" > $timestamp_log
}

Vì tệp timestamp.log được cập nhật (và được tạo nếu tệp không tồn tại) khi tập lệnh thoát, nên lần đầu tiên tập lệnh chạy, tệp timestamp.log sẽ không tồn tại. Điều đó sẽ gây ra sự cố khi tập lệnh cố gắng đọc từ tệp đó.

Để khắc phục vấn đề đó và bảo vệ chống lại các tình huống mà tệp timestamp.log có thể đã bị xóa, chúng tôi kiểm tra sự tồn tại của tệp. Nếu tệp không tồn tại, chúng tôi tạo tệp bằng cách lưu trữ giá trị thời gian giả bằng không vào tệp.

Mã nguồn [Chọn]
# If the timestamp log file doesn't exist, create it
if [ ! -f $timestamp_log ]; then
  set_timestamp 0
fi

Bây giờ chúng ta có thể an toàn lấy nguồn tệp và đọc hướng dẫn bên trong tệp. Thao tác này đặt biến previous_exit thành dấu thời gian trước đó.

Mã nguồn [Chọn]
# get the last exit time as variable called previous_exit
source $timestamp_log

Bây giờ chúng ta đã có giá trị dấu thời gian, chúng ta có thể tính toán khoảng thời gian trung gian giữa dấu thời gian trước đó và thời điểm hiện tại.

Mã nguồn [Chọn]
# get the interim period since the last exit
interim=$(( $(date +%s)-$previous_exit ))

Bây giờ chúng ta có thể thực hiện một thử nghiệm đơn giản để xem liệu đã đủ thời gian để tập lệnh được phép chạy hay chưa. Tôi đang sử dụng một giá trị tùy ý và ngắn là năm giây để thử nghiệm.

Mã nguồn [Chọn]
if (( $interim <= 5 )); then
  echo "Too soon... $interim seconds..."
  exit 1;
fi

# your actual script starts here
echo "Running..."

Nếu khoảng thời gian tạm thời dài hơn năm giây, tập lệnh có thể tiếp tục. Khi hoàn tất, chúng ta ghi thời gian hiện tại vào tệp timestamp.log bằng cách gọi hàm set_timestamp.

Mã nguồn [Chọn]
# set the new timestamp
set_timestamp $(date +%s)

exit 0

Sau đây là toàn bộ kịch bản.

Mã nguồn [Chọn]
#!/bin/bash

# location of the timestamp log file
timestamp_log="/home/dave/.timestamp.log"

set_timestamp() {
  echo "previous_exit=$1" > $timestamp_log
}

# If the timestamp log doesn't exist, create it
if [ ! -f $timestamp_log ]; then
  set_timestamp 0
fi

# get the last exit time as a variable called previous_exit
source $timestamp_log

# get the interim period since the last exit
interim=$(( $(date +%s)-$previous_exit ))

if (( $interim <= 5 )); then
    echo "Too soon... $interim seconds..."
  exit 1;
fi

# set the new timestamp
set_timestamp $(date +%s)

echo "Running..."

exit 0

Sao chép điều này vào trình soạn thảo yêu thích của bạn và lưu nó dưới dạng tệp có tên là   Đăng nhập để xem liên kết. Nhớ thay đổi giá trị timestamp_log= ở dòng 4 để trỏ đến vị trí trên máy tính của bạn nơi timestamp.log sẽ được lưu trữ.

Làm cho tập lệnh của bạn có thể thực thi được.

Mã nguồn [Chọn]
chmod +x tc.sh

Và bây giờ chúng ta có thể chạy nó.

Mã nguồn [Chọn]
./tc.sh

Các lần thực hiện tiếp theo trong khoảng thời gian loại trừ năm giây sẽ tự chấm dứt. Sau năm giây trôi qua, chúng ta có thể chạy lại tập lệnh.

6. Hữu ích trong các tình huống khác

Hãy nhớ rằng, nếu tập lệnh của bạn có thực thi phân nhánh và có thể thoát ở các điểm khác nhau trong tập lệnh, bạn sẽ cần gọi set_timestamp trước mỗi lần thoát có thể xảy ra. Đó là lý do tại sao việc tạo hàm set_timestamp là đáng giá, mặc dù nó chỉ được sử dụng hai lần trong tập lệnh này.

Thủ thuật lưu trữ tên biến và giá trị của nó trong một tệp có nguồn, có thể được tận dụng để đọc trong tệp cấu hình. Bạn chỉ cần viết danh sách tên biến và giá trị của chúng vào một tệp và lấy nguồn từ tập lệnh của bạn.