Làm cách nào để xóa các tệp khỏi thư mục có hơn 60 tệp trong unix?


7

Tôi muốn đặt một tập lệnh trong cronjob sẽ chạy trong một thời gian cụ thể và nếu số tập tin lớn hơn 60, nó sẽ xóa các tập tin cũ nhất khỏi thư mục đó. Lần cuối ra mắt. Tôi đã thử,

#!/bin/ksh  
for dir in /home/DABA_BACKUP  
do  
    cd $dir  
    count_files=`ls -lrt | wc -l`   
    if [ $count_files -gt 60 ];  
    then  
        todelete=$(($count_files-60))  
        for part in `ls -1rt`  
        do  
            if [ $todelete -gt 0 ]  
            then  
                rm -rf $part  
                todelete=$(($todelete-1))  
            fi  
        done  
    fi
done   

Đây là tất cả các tập tin sao lưu được lưu hàng ngày và được đặt tên backup_$date. Được không


1
Lưu ý: Để chỉ đếm các tệp bạn không cần lstùy chọn -lrtvà để tạo danh sách trong vòng lặp for bạn không cần lstùy chọn -1. Mở rộng biến miễn phí ( "$dir""$part") nên được trích dẫn. Thay vì sử dụng backtics $(ls | wc -l).
Janis

@Janis vẫn sẽ thất bại nếu tên tệp chứa dòng mới.
terdon

1
@Vâng tôi biết. Có quá nhiều thứ đáng để sửa ở đó.
Janis

Kịch bản của tôi là ok ... Tôi chỉ chỉnh sửa theo câu trả lời cuối cùng. Bây giờ nó đang xóa các tệp khỏi thư mục có số lượng tệp lớn hơn 60. Tệp cuối cùng được nhập và tệp đầu tiên bị xóa khỏi thư mục. Đó là những gì tôi muốn, Lần đầu tiên ra mắt.
pmaipmui

Nó không ổn. Nó sẽ bị hỏng nếu tên tệp của bạn chứa dấu cách hoặc dòng mới. Nó cũng phức tạp hơn nhiều so với cần thiết. Tên của bạn trong định dạng nào? Bạn nói backup_$datenhưng là $dategì? Phải 114-06-2015không Hay là Sun Jun 14 15:06:53 EEST 2015? Nếu bạn cho chúng tôi biết chính xác nó là gì, chúng tôi có thể cung cấp cho bạn cách mạnh mẽ và hiệu quả hơn để làm điều này.
terdon

Câu trả lời:


3

Không, đối với một điều, nó sẽ phá vỡ tên tập tin có chứa dòng mới. Nó cũng phức tạp hơn mức cần thiết và có tất cả những nguy hiểm của việc phân tích cú pháp ls .

Một phiên bản tốt hơn sẽ là (sử dụng các công cụ GNU):

#!/bin/ksh  
for dir in /home/DABA_BACKUP/*
do
    ## Get the file names and sort them by their
    ## modification time
    files=( "$dir"/* );
    ## Are there more than 60?
    extras=$(( ${#files[@]} - 60 ))
    if [ "$extras" -gt 0 ]
    then
    ## If there are more than 60, remove the first
    ## files until only 60 are left. We use ls to sort
    ## by modification date and get the inodes only and
    ## pass the inodes to GNU find which deletes them
    find dir1/ -maxdepth 1 \( -inum 0 $(\ls -1iqtr dir1/ | grep -o '^ *[0-9]*' | 
        head -n "$extras" | sed 's/^/-o -inum /;' ) \) -delete
    fi
done

Lưu ý rằng điều này giả định rằng tất cả các tệp nằm trên cùng một hệ thống tệp và có thể cho kết quả không mong muốn (chẳng hạn như xóa các tệp sai) nếu không. Nó cũng sẽ không hoạt động tốt nếu có nhiều liên kết cứng trỏ đến cùng một nút.


Rất cám ơn @terdon. Tôi vừa sửa đổi kịch bản của tôi theo giải pháp của bạn. Nó hoạt động trơn tru. Cảm ơn tất cả mọi người cho bạn những nỗ lực có giá trị. Bạn có thể vui lòng giúp tôi không? Nếu có thể, vui lòng chia sẻ một số liên kết để viết kịch bản shell.
pmaipmui

1
Tôi khó có thể tin rằng định dạng ngày mà Nainita đã đề cập (130615, 140615) được tự động sắp xếp tốt như bạn giả định ... Hãy thử với ngày 140615 và 130715. Đầu ra mặc định sẽ là 130715 sau đó là 140615.
Lambert

1
@mikeerv bạn nâng hai điểm hợp lệ. Là sự châm biếm lén lút thực sự cần thiết để làm cho họ? Tại sao bạn phải biến mọi thứ thành một cuộc chiến? Tất cả những gì bạn phải làm là chỉ ra những sai lầm của tôi và tôi sẽ vui vẻ thừa nhận chúng nhưng bạn đã chọn tấn công thay vì dạy.
terdon

1
@mikeerv bạn đã ngớ ngẩn bây giờ và nó đã được mở ra cho. Chắc chắn bây giờ bạn biết tôi hoàn toàn không có vấn đề gì khi thừa nhận tôi đã sai. Và tôi đã rất sai ở đây. Tất cả bạn phải làm là chỉ ra nó. Dù sao, xem câu trả lời cập nhật, bạn sẽ thích nó, nó phân tích cú pháp ls.
terdon

1
@mikeerv Tôi không sử dụng -l. Tôi cũng không biết tại sao bạn lại nhắc đến Solaris. Tôi quen thuộc với ý kiến ​​của bạn về bài đăng đó như bạn phải ở với tôi. Chúng ta đừng làm lại nó. Tôi là findvì đó là cách tốt nhất mà tôi biết để xóa các tập tin bằng inodes. Tôi rất vui khi nghe về một cái tốt hơn (và điều đó sẽ đưa ra một nhận xét thực sự mang tính xây dựng). Và vâng, đây không phải là một câu trả lời hay và tôi không muốn nó được chấp nhận (và tôi đã viết nó trước khi xem bình luận cuối cùng của bạn). Vì nó được chấp nhận, tuy nhiên, ít nhất tôi đã cố gắng làm cho nó i) hoạt động, không giống như phiên bản trước và ii) mạnh mẽ.
terdon

3
#! /bin/zsh -
for dir (/home/DABA_BACKUP/*) rm -f $dir/*(Nom[61,-1])

Đối với những người không biết gì về zsh ;-):

  • for var (list) cmd: phiên bản ngắn của for var in list; do cmd; donevòng lặp (gợi nhớ perlcú pháp).
  • $dir: zshcác biến không cần được trích dẫn giống như chúng làm trong các shell khác như zshcó các toán tử rõ ràng splitglobdo đó không thực hiện phân tách ngầm định + global khi mở rộng tham số.
  • *(...): toàn cầu với vòng loại toàn cầu :
  • N:: nullglobtoàn cầu mở rộng thành không có gì thay vì gây ra lỗi khi nó không khớp.
  • m: o rder các tệp được tạo vào thời gian m m (lần đầu tiên trẻ nhất).
  • [61,-1]: từ danh sách được sắp xếp đó, chọn thứ 61 đến cuối cùng.

Vì vậy, về cơ bản loại bỏ tất cả trừ 60 tập tin trẻ nhất.


Bạn có thể giải thích điều đó cho những người không biết gì về zsh? Tôi giả sử bạn đang sắp xếp theo ngày nào đó để bạn không gặp phải vấn đề mà câu trả lời của tôi phải không? Đó là những gì NOmkhông?
terdon

@terdon, xem chỉnh sửa. Tôi thực sự đã có logic sai (đảo ngược). Nên omsắp xếp với người trẻ nhất trước (như trong ls -t).
Stéphane Chazelas

Rất đẹp, cảm ơn! Bạn có thể có một cái nhìn vào câu trả lời cập nhật của tôi. Tôi nghĩ rằng tôi nên làm việc ngay bây giờ và ii) mạnh mẽ với bất kỳ tên tệp nào. Tôi đánh giá cao nếu bạn có thể chỉ ra bất kỳ tên tệp nào sẽ phá vỡ nó.
terdon

1

Để có được danh sách các mục cũ nhất cần xóa (do đó giữ 60 mục mới nhất):

ls -t | tail -n +61

Lưu ý rằng vấn đề chính của phương pháp của bạn vẫn được giải quyết ở đây: cách xử lý tệp với dòng mới, trong trường hợp có vấn đề; nếu không, bạn chỉ có thể sử dụng (thay thế chương trình khá phức tạp của bạn):

cd /home/DABA_BACKUP || exit 1
ls -t | tail -n +61 | xargs rm -rf


Lưu ý: Vì dường như bạn có các bản sao lưu hàng ngày, bạn cũng có thể sử dụng một cách tiếp cận dựa trên ngày của tệp và find; như trong:

find /home/DABA_BACKUP -mtime +60 -exec ls {} +

(trong đó lslệnh sẽ - sau khi kiểm tra cẩn thận thao tác chính xác! - được thay thế bằng rmlệnh thích hợp ).


1
Lưu ý rằng việc sử dụng xargscũng giả sử tên tệp không chứa dấu cách, tab, dòng mới (các dạng ký tự trống khác tùy thuộc vào cách thực hiện ngôn ngữ và xargs), trích dẫn đơn, trích dẫn kép và dấu gạch chéo ngược. Bạn có thể muốn thêm một - vào cmdline rm để tránh các vấn đề với các tệp có tên bắt đầu bằng -. (có lẽ không phải là vấn đề với OP nhưng đáng chú ý ở đây cho bất kỳ ai đến đây có nhu cầu tương tự).
Stéphane Chazelas

1
rm60()( IFS=/; set -f; set $(
        set +f; \ls -1drt ./*)
        while shift &&
              [ $# -gt 60 ]
        do    [ -d "${1%?.}" ] ||
              rm "./${1%?.}"   || exit
        done
)

Điều này sẽ làm việc cho bạn. Nó sẽ xóa các tệp cũ nhất trong thư mục hiện tại lên tới 60. Nó sẽ thực hiện điều này bằng cách phân tích cú pháp ls mạnh mẽ và nó sẽ thực hiện mà không đưa ra bất kỳ giả định nào về tên tệp của bạn - chúng có thể được đặt tên theo bất kỳ ngày nào và không cần đặt tên theo ngày. Điều này sẽ chỉ hoạt động cho một danh sách của thư mục hiện tại và trong trường hợp bạn đã lscài đặt POSIX (và không bị che bởi một số chức năng vỏ xấu, nhưng aliases được bảo hiểm) .

Giải pháp trên chỉ áp dụng một số phân tách shell rất cơ bản cho một số tên đường dẫn Unix rất cơ bản. Nó đảm bảo lsliệt kê tất cả các tệp không chấm trong thư mục hiện tại mỗi dòng như sau:

./oldestfile
./second-oldestfile

Bây giờ, bất kỳ một trong số chúng cũng có thể có dòng mới ở giữa, nhưng đó không phải là một vấn đề. Bởi vì trong trường hợp đó, chúng sẽ được liệt kê như sau:

./oldest
file
./s
econd

old
est
file
./third

...và như thế. Và những dòng mới không làm phiền chúng tôi - vì chúng tôi không chia rẽ chúng. Tại sao chúng ta? Chúng tôi đang làm việc với các tên đường dẫn, chúng ta nên phân tách trên dấu phân cách đường dẫn và đó là những gì chúng ta làm:IFS=/ .

Bây giờ làm việc ra một chút lạ. Chúng tôi kết thúc với một danh sách đối số trông như thế này:

<.> <file1\n.> <file2\n.> ... <filelast>

... nhưng điều đó thực sự rất tốt cho chúng tôi, bởi vì chúng tôi có thể trì hoãn các đối số của mình được xử lý bởi shell như các tệp (hoặc, trong trường hợp chúng tôi muốn tránh, các liên kết tượng trưng) cho đến khi chúng tôi khá sẵn sàngrm chúng.

Vì vậy, một khi chúng tôi đã có danh sách tệp của mình, tất cả những gì chúng tôi phải làm là shiftloại bỏ đối số đầu tiên của chúng tôi, hãy kiểm tra xem chúng tôi hiện có hơn 60 đối số, có thể từ chối rmthư mục con (mặc dù, tất nhiên, điều đó hoàn toàn phụ thuộc vào bạn) và nếu không thì rmđối số đầu tiên của chúng tôi ít hơn hai ký tự cuối cùng của nó. Chúng ta không phải lo lắng về lần cuối cùng tranh luận - không có thời gian được nối thêm - bởi vì chúng ta không bao giờ đến đó, và thay vào đó bỏ cuộc ở tuổi 60. Nếu chúng ta đã đi xa đến mức lặp đi lặp lại thì chúng ta chỉ cần thử lại và lặp qua danh sách arg theo cách này cho đến khi chúng tôi cắt tỉa nó để thỏa mãn.

Làm thế nào để phá vỡ? Theo hiểu biết của tôi thì không, nhưng tôi đã cho phép - nếu bất cứ lúc nào xảy ra lỗi không mong muốn thì vòng lặp bị phá vỡ và hàm trả về khác 0.

Và do đó, lscó thể làm danh sách của bạn cho bạn trong thư mục hiện tại mà không có bất kỳ vấn đề nào cả. Bạn có thể mạnh mẽ cho phép nó sắp xếp các đối số của bạn cho bạn, miễn là bạn có thể phân định chúng một cách đáng tin cậy. Vì lý do đó, nó sẽ không hoạt động như được viết cho bất cứ thứ gì ngoại trừ thư mục hiện tại - nhiều hơn một dấu phân cách trong chuỗi đường dẫn sẽ yêu cầu một mức phân định khác, có thể được thực hiện bằng cách đưa nó ra ngoài gấp đôi cho tất cả trừ trường cuối cùng vào các trường NUL , nhưng tôi không quan tâm để làm điều đó bây giờ.


-1

Nếu bạn biết, tất cả các tệp đều có tên backup_ *, bạn nên đưa nó vào lệnh ls, vì vậy bạn chỉ xử lý những tệp đó chứ không phải các tệp vô tình rơi vào thư mục. Sau đó ls được sử dụng trong một đường ống, nó chỉ liệt kê 1 tệp trên mỗi dòng và sau đó chỉ đếm, không cần sắp xếp, vì vậy

count_files=$(ls -U backup_* | wc -l)

for part in $(ls -rt backup_*);do
    rm -rf "$part"
    todelete=$(($todelete-1))
    if [[ $todelete -eq 0 ]]; then
        break
    fi
done

1
Nói chung, nên tránh phân tích cú pháp lsđầu ra trong các tập lệnh. Bạn có thể sử dụng findthay thế.
Erathiel

@Erathiel - chính xác những gì findcung cấp ở đây nên được ưa thích ls? Một lần, ai đó đã viết một bài đăng trên blog khá lỗi về phân tích cú phápls và vì một lý do nào đó, toàn bộ cộng đồng linux đối xử với nó như Ngũ kinh. Hãy nhìn xem, một vài điểm hợp lệ được thực hiện trong bài đăng trên blog cũng áp dụng như nhau findtrong trường hợp này.
mikeerv
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.