Điểm:18

Ubuntu tự động xóa tệp cũ nhất trong thư mục khi dung lượng đĩa trên 90%, lặp lại cho đến khi dung lượng dưới 80%

lá cờ bv

Tôi đã tìm thấy một vài tập lệnh công việc định kỳ tương tự nhưng không có chính xác cách tôi cần chúng và tôi không biết đủ về tập lệnh cho Linux để thử sửa đổi mã khi thực hiện loại công việc có thể trở thành thảm họa này.

Về cơ bản, tôi có camera ip ghi vào /home/ben/ftp/giám sát/ nhưng tôi cần đảm bảo luôn có đủ dung lượng trên đĩa để làm như vậy.

Ai đó vui lòng có thể hướng dẫn tôi cách tôi có thể thiết lập một công việc định kỳ để:

Kiểm tra nếu /dev/sbd/ đã đạt 90% công suất. Nếu vậy thì hãy xóa tệp cũ nhất trong (và các tệp trong thư mục con) /home/ben/ftp/giám sát/ Và lặp lại điều này cho đến khi /dev/sbd/ dung lượng dưới 80% Lặp lại sau mỗi 10 phút.

J... avatar
lá cờ in
Điều gì sẽ xảy ra nếu `/dev/sdb` (lưu ý : `sdb` != `sbd` - theo dõi lỗi chính tả trong tập lệnh của bạn!) đầy >80% bất kể nội dung của `/home/ben/ftp/surveillance/` ? Điều này sẽ luôn xóa tất cả các bản ghi của bạn, không để lại gì cho bạn. Tốt hơn nên có các camera quan sát ghi vào một ổ đĩa chuyên dụng không được chia sẻ với (tức là) hệ điều hành của bạn hoặc bất kỳ người dùng nào khác. Lý tưởng nhất là các camera nên tự quản lý việc này, biết được tệp nào là của chúng và ghi đè lên các tệp cũ nhất của chính chúng khi dung lượng ổ đĩa chuyên dụng đầy.
lá cờ bv
Điểm tốt J... Thư mục ftp của tôi thực sự là một ổ đĩa ngoài được gắn trong thư mục nhà của tôi và hiện được dành riêng cho các bản ghi cam. Tôi không bận tâm đến việc thay đổi cấu trúc thư mục mà trước đây nó là một đĩa chuyên dụng. Các máy ảnh cũng tải lên một ảnh tĩnh .jpg của mọi bản ghi, vì vậy tôi sẽ thiết lập một cron khác để xóa tất cả .jpg theo định kỳ.
Điểm:33
lá cờ in

Viết những loại kịch bản này cho mọi người luôn khiến tôi lo lắng bởi vì trong trường hợp có bất cứ điều gì không ổn, một trong ba điều sẽ xảy ra:

  1. Tôi sẽ tự đá mình vì lỗi đánh máy cấp độ n00b
  2. Các mối đe dọa về cái chết sẽ đến với tôi vì ai đó đã sao chép/dán một cách mù quáng mà không:
    • cố gắng hiểu kịch bản
    • thử nghiệm kịch bản
    • có một dự phòng hợp lý tại chỗ
  3. Tất cả những điều trên

Vì vậy, để giảm rủi ro của cả ba, đây là bộ công cụ dành cho bạn:

#!/bin/sh
DIR=/home/ben/ftp/giám sát
HÀNH ĐỘNG=90
df -k $DIR | grep -vE '^Filesystem' | awk '{ in $5 " " $1 }' | trong khi đầu ra đọc;
làm
  tiếng vang $ đầu ra
  usep=$(echo $output | awk '{ print $1}' | cut -d'%' -f1 )
  phân vùng=$(echo $output | awk '{ print $2 }' )
  nếu [ $usep -ge $ACT ]; sau đó
    echo "Hết dung lượng \"$partition ($usep%)\" trên $(hostname) như trên $(date)"
    oldfile=$(ls -dltr $DIR/*.gz|awk '{ print $9 }' | head -1)
    echo "Hãy xóa \"$oldfile\" ..."
  fi
xong

NHỮNG ĐIỀU CẦN LƯU Ý:

  1. Kịch bản này không xóa gì

  2. TRỰC TIẾP là thư mục để làm việc với

  3. HÀNH ĐỘNG là tỷ lệ phần trăm tối thiểu cần thiết để hành động

  4. Chỉ một tệp â cũ nhất được chọn để "xóa"

  5. Bạn sẽ muốn thay thế *.gz với loại tệp thực tế của video giám sát của bạn.
    KHÔNG ĐƯỢC DÙNG *.* HOẶC * BỞI BẢN THÂN!

  6. Nếu phân vùng chứa TRỰC TIẾP có công suất lớn hơn HÀNH ĐỘNG, bạn sẽ thấy một thông báo như thế này:

    97% /dev/sda2
    Hết dung lượng "/dev/sda2 (97%)" trên ubuntu-vm kể từ Thứ Tư ngày 12 tháng 1 07:52:20 UTC 2022
    Hãy xóa "/home/ben/ftp/surveillance/1999-12-31-video.gz" ...
    

    Một lần nữa, kịch bản này sẽ không xóa bất cứ thứ gì.

  7. Nếu bạn hài lòng với đầu ra, thì bạn có thể tiếp tục sửa đổi tập lệnh để xóa/di chuyển/lưu trữ khi bạn thấy phù hợp

Kiểm tra thường xuyên. Kiểm tra tốt. Và hãy nhớ rằng: Khi đặt rừm trong một kịch bản, không có hoàn tác.

lá cờ ao
Thật ngớ ngẩn khi chuyển `grep` thành `awk`, bạn chỉ cần thêm `!/^Filesystem/` vào đầu lệnh awk. Hoặc bạn có thể yêu cầu `df` chỉ tạo đầu ra mà bạn muốn và sử dụng `sed` để loại bỏ tiêu đề và dấu phần trăm: `usep=$(df --output=pcent $DIR | sed '1d;s/%/%/ /')`
Peter Cordes avatar
lá cờ fr
`echo $output` nên trích dẫn `"$output"` vì bạn không thực sự cần nó để tách từ. Điều đó có thể ổn, nhưng ngay cả trên các hệ thống bình thường, một số phương tiện di động cuối cùng được gắn trên các đường dẫn có dấu cách trong tên của chúng, điều này có thể dẫn đến thông báo lỗi từ điều này trong thực tế. Vì vậy, quy tắc ngón tay cái, luôn trích dẫn các phần mở rộng biến trừ khi bạn đặc biệt muốn tách từ hoặc một số hiệu ứng khác mà trích dẫn các khối.
lá cờ ua
Tương tự, nhưng sử dụng tính năng `firstaction` của `logrotate`: https://serverfault.com/questions/372809/free-space-Driven-log-rotation-on-linux
Eric Duminil avatar
lá cờ us
@NickMatteo Nếu mã tương ứng hoạt động tốt và có thể đọc được, thì thật ngớ ngẩn khi tranh luận rằng nó ngớ ngẩn vì nó có thể được viết khác với ngôn ngữ 45 tuổi và không dễ đọc lắm. Các đường dẫn UNIX rất tuyệt vời và có thể đọc được, ai quan tâm liệu lệnh có thể được viết bằng một hoặc hai đường dẫn ít hơn không?
lá cờ ao
@EricDuminil: Cách sử dụng hơi phổ biến của `grep 'PATTERN' | awk '{làm công cụ để khớp các dòng}'` về mặt khách quan là ngớ ngẩn, vì toàn bộ mục đích của awk là làm công cụ cho các dòng phù hợp và bạn có thể chỉ cần viết `awk '/PATTERN/ {dostuff}'`.
Eric Duminil avatar
lá cờ us
@NickMatteo: Bạn có thể tự do sử dụng bất kỳ công cụ nào mà bạn quen thuộc. matigo dường như hài lòng với `grep ... | awk ...`, bạn sẽ hài lòng với `awk '/PATTERN/ {dostuff}`, và tôi sẽ hài lòng với một tập lệnh Ruby hoặc Python ngắn. Miễn là các tập lệnh đang hoạt động và có thể đọc được, không có ai sai và không có lệnh nào là ngớ ngẩn, và chắc chắn không phải là ngớ ngẩn về mặt khách quan.
lá cờ cn
@EricDuminil Họ có thể không ngớ ngẩn một cách khách quan, nhưng có một lập luận rằng trang web Hỏi & Đáp nên dạy mọi người cách hoạt động của các lệnh. Đường ống `awk` làm cho có vẻ như lệnh không thể tự xử lý.Cũng có giá trị khi không sử dụng nhiều lệnh thông qua đường ống và làm chậm quá trình. Nói tóm lại, có các tiêu chí để tranh luận rằng sử dụng `awk` theo mục đích sử dụng là Câu trả lời tốt hơn.
lá cờ bv
@matgio - Tôi không thể làm cho `oldfile=$(ls -dltr $DIR/*.mp4|awk '{ print $9 }' | head -1)` hoạt động nhưng tôi đã làm cho nó hoạt động được `oldfile=$( tìm $DIR -name "*.mp4" -type f | sort | head -n 1)` Nếu có bất kỳ lý do phương pháp của tôi không nên được sử dụng?
lá cờ in
@Beno â Bạn có gặp lỗi với lệnh đầu tiên không? Dù bằng cách nào, nếu `find` sẽ cung cấp cho bạn thứ bạn cần, hãy thoải mái sử dụng thứ đó. Kịch bản chỉ là "bộ công cụ khởi động" để giúp bạn bắt đầu với một số nội dung sơ bộ
Điểm:12
lá cờ kz

Tôi sẽ sử dụng Python cho nhiệm vụ đó. Nó có thể dẫn đến nhiều mã hơn một giải pháp bash thuần túy, nhưng:

  • nó (IMO) dễ kiểm tra hơn, chỉ cần sử dụng người khó tính hoặc thống nhất mô-đun
  • nó có thể đọc được đối với những người không sử dụng Linux (ngoại trừ get_device chức năng dành riêng cho Linux ...)
  • bắt đầu dễ dàng hơn (một lần nữa IMO)
  • Nếu bạn muốn gửi một số email thì sao? Để kích hoạt hành động mới? Các tập lệnh có thể được làm giàu dễ dàng bằng ngôn ngữ lập trình như Python.

Kể từ Python 3.3, đóng cửa mô-đun đi kèm với một chức năng có tên disk_usage. Nó có thể được sử dụng để lấy mức sử dụng đĩa dựa trên một thư mục nhất định.

Vấn đề nhỏ là tôi không biết cách dễ dàng lấy tên của đĩa, I.E, /dev/sdb, mặc dù có thể sử dụng đĩa của nó (sử dụng bất kỳ thư mục nào được gắn trên /dev/sdb, trong trường hợp của tôi $HOME Ví dụ). Tôi đã viết một chức năng gọi là get_device vì mục đích này.

#!/usr/bin/env python3
nhập argparse
từ os.path nhập getmtime
từ Shutil nhập disk_usage, rmtree
từ thoát nhập sys
từ đường dẫn nhập pathlib
từ gõ nhập Iterator, Tuple


def get_device(path: Path) -> str:
    """Tìm phần gắn kết cho một thư mục nhất định. Điều này chỉ cần thiết cho mục đích ghi nhật ký."""
    # Đọc /etc/mtab để tìm hiểu về các điểm gắn kết
    mtab_entries = Đường dẫn("/etc/mtab").read_text().splitlines()
    # Tạo một lệnh của các điểm và thiết bị gắn kết
    mount_points = dict([list(reversed(line.split(" ")[:2])) cho dòng trong mtab_entries])
    # Tìm điểm gắn kết của đường dẫn đã cho
    trong khi path.resolve(True).as_posix() không có trong mount_points:
        đường dẫn = đường dẫn.parent
    # Trả lại thiết bị được liên kết với điểm gắn kết
    trả về mount_points[path.as_posix()]


def get_directory_and_device(path: str) -> Tuple[str, Path]:
    """Thoát quá trình nếu thư mục không tồn tại."""
    fs_path = Đường dẫn(đường dẫn)
    # Đường dẫn phải tồn tại
    nếu không fs_path.exists():
        print(f"ERROR: Không có thư mục nào như vậy: {path}")
        thoát(1)
    # Và đường dẫn phải là một thư mục hợp lệ
    nếu không fs_path.is_dir():
        print(f"Đường dẫn phải là một thư mục chứ không phải tệp: {path}")
        thoát(1)
    # Lấy thiết bị
    thiết bị = get_device(fs_path)

    trả lại thiết bị, fs_path


def get_disk_usage(path: Path) -> float:
    # Shutil.disk_usage hỗ trợ Đường dẫn giống như các đối tượng nên không cần chuyển thành chuỗi
    cách sử dụng = disk_usage(đường dẫn)
    # Nhận mức sử dụng đĩa theo tỷ lệ phần trăm
    trả lại mức sử dụng.đã sử dụng/sử dụng.tổng * 100


def remove_file_or_directory(path: Path) -> Không có:
    """Xóa đường dẫn đã cho, có thể là thư mục hoặc tệp."""
    # Xóa các tệp
    nếu đường dẫn.is_file():
        đường dẫn.unlink()
    # Xóa đệ quy cây thư mục
    nếu đường dẫn.is_dir():
        rmtree(đường dẫn)


def find_oldest_files(
    đường dẫn: Đường dẫn, mẫu: str = "*", ngưỡng: int = 80
) -> Trình lặp [Đường dẫn]:
    """Lặp lại trên các tệp hoặc thư mục có trong một thư mục khớp với mẫu đã cho."""
    # Liệt kê các tệp trong thư mục được nhận làm đối số và sắp xếp chúng theo độ tuổi
    tệp = đã sắp xếp (path.glob (mẫu), key = getmtime)
    # Đường dẫn tệp lợi nhuận cho đến khi mức sử dụng thấp hơn ngưỡng
    cho tệp trong tệp:
        cách sử dụng = get_disk_usage(đường dẫn)
        nếu mức sử dụng < ngưỡng:
            nghỉ
        tập tin năng suất


chắc chắn check_and_clean(
    đường dẫn: str,
    ngưỡng: int = 80,
    xóa: bool = Sai,
) -> Không có:
    """Chức năng chính"""
    thiết bị, fspath = get_directory_and_device(path)
    # Shutil.disk_usage hỗ trợ Đường dẫn giống như các đối tượng nên không cần chuyển thành chuỗi
    cách sử dụng = disk_usage(đường dẫn)
    # Hành động nếu cần
    nếu mức sử dụng > ngưỡng:
        in(
            f"Mức sử dụng đĩa lớn hơn ngưỡng: {usage:.2f}% > {threshold}% ({device})"
        )
    # Lặp lại các tệp để xóa
    đối với tệp trong find_oldest_files(fspath, "*", ngưỡng):
        print(f"Xóa tệp {file}")
        nếu loại bỏ:
            remove_file_or_directory(file)


def main() -> Không có:

    trình phân tích cú pháp = argparse.ArgumentParser(
        description="Dọn sạch các tập tin cũ khi mức sử dụng đĩa vượt quá giới hạn."
    )

    trình phân tích cú pháp.add_argument(
        "path", help="Đường dẫn thư mục mà các tập tin sẽ bị xóa", gõ=str
    )
    trình phân tích cú pháp.add_argument(
        "--ngưỡng",
        "-t",
        metavar="T",
        help="Ngưỡng sử dụng theo tỷ lệ phần trăm",
        loại = int,
        mặc định = 80,
    )
    trình phân tích cú pháp.add_argument(
        "--gỡ bỏ",
        "--rm",
        help="Các tập tin không bị xóa trừ khi tùy chọn --removed hoặc --rm được chỉ định",
        hành động="store_true",
        mặc định=Sai,
    )

    args = trình phân tích cú pháp.parse_args()

    check_and_clean(
        args.path,
        ngưỡng=args.threshold,
        loại bỏ = args. loại bỏ,
    )


nếu __name__ == "__main__":
    chủ yếu()

Nếu bạn cần sắp xếp nhiều tác vụ bằng CRON, bạn nên tập hợp một số mã Python làm thư viện và sử dụng lại mã này trong nhiều tác vụ.

CHỈNH SỬA: Cuối cùng tôi đã thêm phần CLI vào tập lệnh, tôi nghĩ tôi sẽ tự mình sử dụng nó

qwr avatar
lá cờ kr
qwr
fwiw bạn có thể gửi email từ dòng lệnh.
gcharbon avatar
lá cờ kz
Tôi không nói rằng nó không thể được thực hiện trong CLI, tôi đang nói rằng OP không quen với bash và anh ấy có thể thấy việc đó dễ dàng hơn trong Python
lá cờ bv
Cảm ơn cho bài viết toàn diện! Tôi thích cách tiếp cận dễ đọc hơn nhưng không may là tôi cũng không quen thuộc lắm với phython. Nhưng bạn đã khiến tôi nhận ra rằng tôi có thể làm điều này với php mà tôi cảm thấy thoải mái hơn rất nhiều.
Clumsy cat avatar
lá cờ cn
+1 để kiểm tra thứ gì đó dễ dàng hơn. Tôi muốn kiểm tra đơn vị cho một tập lệnh như thế này.
Điểm:1
lá cờ tj

Kiểm tra nếu /dev/sbd/ đã đạt 90% công suất. Nếu vậy thì hãy xóa tệp cũ nhất trong (và các tệp trong thư mục con) /home/ben/ftp/giám sát/ Và lặp lại điều này cho đến khi /dev/sbd/ dung lượng dưới 80% Lặp lại sau mỗi 10 phút.

Tập lệnh bên dưới sẽ thực hiện chính xác điều đó (với điều kiện bạn thêm nó vào crontab để chạy trong khoảng thời gian 10 phút). Hãy chắc chắn hơn rằng đây là điều bạn thực sự muốn làm, vì điều này có thể dễ dàng xóa tất cả các tập tin trong /home/ben/ftp/giám sát/ nếu đĩa của bạn đang đầy ở đâu đó bên ngoài thư mục này.

#!/bin/sh
thư mục = '/ home/ben/ftp/giám sát'
max_usage=90
target_usage=80
[ -d "$directory" ] || thoát 1
[ "$max_usage" -gt "$goal_usage" ] || thoát 1
[ "$( df --output=pcent $directory | \
    grep -Ewo '[0-9]+' )" -ge "$max_usage" ] || thoát 0
dev_used="$( df -B 1K --output=used $directory | \
    grep -Ewo '[0-9]+' )"
target_usage="$( printf "%.0f" \
    $( echo ".01 * $goal_usage * \
    $( df -B 1K --output=size $directory | \
        grep -Ewo '[0-9]+' )" | bc ) )"
echo "$( find $directory -type f -printf '%Ts,%k,\047%p\047\n' )" | \
    sắp xếp -k1 | \
        awk -F, -v Goal="$(($dev_used-$goal_usage))" '\
            (sum+$2)>bàn thắng{printf "%s ",$3; lối ra} \
            (sum+$2)<=goal{printf "%s ",$3}; {tổng+=$2}' | \
                xargs rm

Cách thức hoạt động của tập lệnh này:

3 dòng đầu tiên sau shebang là các biến theo tham số của bạn:

  • danh mục là đường dẫn đầy đủ đến thư mục mẹ chứa các tệp và thư mục con mà bạn muốn xóa các tệp cũ (tức là, /home/ben/ftp/giám sát). Các trích dẫn xung quanh giá trị này là không cần thiết trừ khi đường dẫn chứa khoảng trắng.
  • max_usage là phần trăm dung lượng đĩa sẽ kích hoạt các hành động xóa tệp cũ (tức là, 90 phần trăm).
  • mục tiêu_sử dụng là phần trăm dung lượng đĩa bạn muốn đạt được sau khi xóa các tệp cũ (nghĩa là 80 phần trăm).

Lưu ý rằng các giá trị của max_usagemục tiêu_sử dụng cần phải số nguyên.

[ -d "$directory" ] || thoát 1
  • Kiểm tra xem danh mục tồn tại, nếu không thì tập lệnh kết thúc và thoát với trạng thái 1.
[ "$max_usage" -gt "$goal_usage" ] || thoát 1
  • Kiểm tra xem max_usage lớn hơn mục tiêu_sử dụng, nếu không thì tập lệnh kết thúc và thoát với trạng thái 1.
[ "$( df --output=pcent $directory | \
    grep -Ewo '[0-9]+' )" -ge "$max_usage" ] || thoát 0
  • Nhận phần trăm dung lượng ổ đĩa hiện tại được sử dụng và kiểm tra xem nó có đáp ứng hoặc vượt quá ngưỡng được đặt bởi max_usage. Nếu không, không cần xử lý thêm để tập lệnh kết thúc và thoát với trạng thái 0.
dev_used="$( df -B 1K --output=used $directory | \
    grep -Ewo '[0-9]+' )"
  • Lấy số kilobyte dung lượng ổ đĩa hiện được sử dụng.
target_usage="$( printf "%.0f" \
    $( echo ".01 * $goal_usage * \
    $( df -B 1K --output=size $directory | \
        grep -Ewo '[0-9]+' )" | bc ) )"
  • Chuyển đổi mục tiêu_sử dụng biến thành kilobyte (chúng tôi sẽ cần thêm giá trị này xuống).
tìm $directory -type f -printf '%Ts,%k,\047%p\047\n'
  • Định vị tất cả các tệp trong danh mục (và trong tất cả các thư mục con của nó) và tạo danh sách các tệp này, mỗi tệp một dòng, được định dạng là dấu thời gian, kích thước tính bằng kilobyte, 'đầy đủ/đường dẫn/đến/tệp'. Lưu ý rằng 'đầy đủ/đường dẫn/đến/tệp' được đặt trong dấu nháy đơn để khoảng trắng trong tên của tệp hoặc thư mục sẽ không gây ra sự cố sau này.
sắp xếp -k1
  • Sắp xếp trước đó tiếng vang'd danh sách các tệp theo dấu thời gian (cũ nhất trước).
awk -F, -v target="$(($dev_used-$goal_usage))"
  • awk tạo một biến nội bộ ghi bàn đó là bằng với sự khác biệt giữa dev_usedmục tiêu_sử dụng - và đây là tổng giá trị kilobyte của các tệp phải được xóa để giảm phần trăm dung lượng đĩa xuống mục tiêu_sử dụng đặt ở đầu tập lệnh.
(sum+$2)>bàn thắng{printf "%s ",$3; lối ra} \
(sum+$2)<=goal{printf "%s ",$3}; {tổng+=$2}'
  • awk (tiếp theo) bắt đầu xử lý danh sách bằng cách giữ tổng các giá trị của trường 2 (kích thước tính bằng kilobyte) và trường in 3 giá trị ('đầy đủ/đường dẫn/đến/tệp') thành một chuỗi được phân tách bằng dấu cách cho đến khi tổng số kilobyte từ trường 2 trở nên lớn hơn ghi bàn, tại điểm nào awk ngừng xử lý các dòng bổ sung.
xargs rm
  • Chuỗi giá trị 'đầy đủ/đường dẫn/đến/tệp' từ awk được dẫn đến xargs mà chạy rừm lệnh sử dụng chuỗi làm đối số của nó. Thao tác này sẽ xóa các tệp đó.

Đăng câu trả lời

Hầu hết mọi người không hiểu rằng việc đặt nhiều câu hỏi sẽ mở ra cơ hội học hỏi và cải thiện mối quan hệ giữa các cá nhân. Ví dụ, trong các nghiên cứu của Alison, mặc dù mọi người có thể nhớ chính xác có bao nhiêu câu hỏi đã được đặt ra trong các cuộc trò chuyện của họ, nhưng họ không trực giác nhận ra mối liên hệ giữa câu hỏi và sự yêu thích. Qua bốn nghiên cứu, trong đó những người tham gia tự tham gia vào các cuộc trò chuyện hoặc đọc bản ghi lại các cuộc trò chuyện của người khác, mọi người có xu hướng không nhận ra rằng việc đặt câu hỏi sẽ ảnh hưởng—hoặc đã ảnh hưởng—mức độ thân thiện giữa những người đối thoại.