Làm cách nào để xóa tất cả trừ 10 tệp mới nhất trong Linux?


49

Tôi đang cố gắng để giữ một thư mục chứa đầy các tệp nhật ký. Hàng đêm, tôi muốn xóa tất cả trừ 10 cái gần đây nhất. Làm thế nào tôi có thể làm điều này trong một lệnh duy nhất?


3
có một cái nhìn tại logrotate
knittl


@michael Làm thế nào tôi có thể là một bản sao của cái đó, nếu cuộc thảo luận của tôi được tạo trước?
dev4life

@ovaherenow "của bạn"? Tôi không thấy tên của bạn trên một trong hai câu hỏi, vì vậy tôi không chắc ý của bạn là gì. Nhận xét của tôi thậm chí không chỉ ra cái nào là trùng lặp với cái khác. Nhưng nó không thành vấn đề - đó chỉ là một bình luận, với một liên kết. Nó có thể được thêm vào một trong hai câu hỏi hoặc cả hai câu hỏi. Tùy thuộc vào người khác - quản trị viên - đánh dấu người này hoặc người kia là bản sao. Không có ánh sáng bất chính được dự định và cũng không nên được nhận thức. Quan trọng hơn câu hỏi đầu tiên là câu hỏi nào có câu trả lời hay nhất - một lần nữa, liên kết tạo điều kiện cho cuộc thảo luận này, nó không xác định câu trả lời.
michael

@michael wow. Điều đó thực sự kỳ quặc. Chủ đề này đã được mở bởi tôi nhưng đó rõ ràng không phải là tên người dùng của tôi. Nhưng tôi đang nhận được thông báo về chủ đề này.
dev4life

Câu trả lời:


58

Đối với một giải pháp di động và đáng tin cậy, hãy thử điều này:

ls -1tr | head -n -10 | xargs -d '\n' rm -f --

Các tail -n -10cú pháp trong một trong những câu trả lời khác dường như không làm việc ở khắp mọi nơi (tức là, không phải trên hệ thống RHEL5 của tôi).

Và sử dụng $()hoặc ``trên dòng lệnh rmcó nguy cơ

  1. tách tên tệp với khoảng trắng và
  2. vượt quá giới hạn ký tự dòng lệnh tối đa.

xargskhắc phục cả hai vấn đề này bởi vì nó sẽ tự động tìm ra có bao nhiêu đối số có thể vượt qua trong giới hạn ký tự và với -d '\n'nó sẽ chỉ phân tách ở ranh giới dòng của đầu vào. Về mặt kỹ thuật, điều này vẫn có thể gây ra sự cố cho tên tệp có dòng mới, nhưng nó ít phổ biến hơn tên tệp có khoảng trắng và cách duy nhất xung quanh dòng mới sẽ phức tạp hơn nhiều, có thể liên quan đến ít nhất là awk, nếu không nói là perl.

Nếu bạn không có xargs (hệ thống AIX cũ, có thể?) Bạn có thể biến nó thành một vòng lặp:

ls -1tr | head -n -10 | while IFS= read -r f; do
  rm -f "$f"
done

Điều này sẽ chậm hơn một chút vì nó sinh ra một rmtệp riêng cho mỗi tệp, nhưng vẫn sẽ tránh được cảnh báo 1 và 2 ở trên (nhưng vẫn phải chịu các dòng mới trong tên tệp).


@HaukeLaging: Từ head(1)trang người đàn ông:-n, --lines=[-]K print the first K lines instead of the first 10; with the leading '-', print all but the last K lines of each file
Isaac Freeman

Trên Solaris 10 headxargsđưa ra lỗi. Các tùy chọn chính xác là head -n 10xargs rm -f. Toàn lệnh làls -1tr ./ | head -n 10 | xargs rm -rf
DmitrySandalov

1
Lưu ý ls -1trlà số MỘT chứ không phải chữ "L".
người dùng linux shonky 27/8/2015

1
Cũng cần lưu ý rằng dòng mới là ký tự hiếm nhưng hợp lệ trong tên tập tin mở rộng. Điều này có nghĩa là nếu bạn có các tệp "đó" và "đó \ nthing", nếu tập lệnh này chạm vào "đó \ nthing", nó sẽ xóa "đó", lỗi khi không thể xóa "điều" và để lại "that \ nthing" (mục tiêu dự định) không bị ảnh hưởng.
Tổng hợp

1
@IsaacFreeman Tôi đã xóa lời khuyên tồi, chúc mừng.
Walf

20

Mã bạn muốn đưa vào tập lệnh của mình là

 rm -f $(ls -1t /path/to/your/logs/ | tail -n +11)

Các -1(số một) tùy chọn in mỗi tập tin trên một dòng duy nhất, để được an toàn. Các -ftùy chọn để rmcho nó để bỏ qua các file không tồn tại khi lslợi nhuận không có gì.


Áp phích gốc, đây. Tôi đã thử lệnh này, nhưng nó không hoạt động - Tôi gặp lỗi "rm: thiếu toán hạng"
user75814

2
Bạn đã sử dụng đúng đường dẫn? Rõ ràng /path/to/your/logschỉ là bù đắp cho bạn để đi vào con đường chính xác. Để tìm lỗi thực hiện các phần ls -t /path/...ls -t /path/... | tail -n +11một mình và kiểm tra xem kết quả có đúng không.
Daniel Böhmer

1
@HaukeLaging Tôi khá chắc chắn rằng bạn có điều gì đó không đúng. Tâm trí +trước số. Nó làm cho tailkhông in n phần tử cuối cùng mà tất cả các phần tử bắt đầu từ thứ n. Tôi đã kiểm tra lại và lệnh chắc chắn chỉ xóa các phần tử cũ hơn 10 tệp gần đây nhất trong mọi trường hợp.
Daniel Böhmer

@halo Thật vậy, xin lỗi.
Hauke ​​Laging

2
Tôi nghĩ rằng câu trả lời của bạn rất nguy hiểm lsin tên tệp chứ không phải đường dẫn nên bạn rm -fsẽ cố xóa các tệp trong thư mục hiện tại
Jürgen Steinblock

14

Rõ ràng phân tích lslà xấu xa .

Nếu mỗi tệp được tạo hàng ngày và bạn muốn giữ các tệp được tạo trong vòng 10 ngày qua, bạn có thể làm:

find /path/to/files -mtime 10 -delete

Hoặc nếu mỗi tệp được tạo tùy ý:

find /path/to/files -maxdepth 1 -type f -printf '%Ts\t%P\n' | sort -n | head -n -10 | cut -f 2- | xargs rm -rf

5
-mtime 10 -deletechỉ xóa các tệp được sửa đổi chính xác 10 ngày trước. Các tập tin cũ hơn sẽ không bị xóa. -mtime +11 -deletesẽ xóa các tệp đã được sửa đổi 11 ngày trở lên để lại 10 ngày cuối cùng của tệp nhật ký.
Markus

1
Tôi nghĩ rằng việc sử dụng %pthay vì %Plà cần thiết trên printfđể các tập tin có đường dẫn đầy đủ. Nếu không rm -rfthì không hoạt động.
jalopaba

9

Một công cụ như logrotate làm điều này cho bạn. Nó làm cho quản lý nhật ký dễ dàng hơn nhiều. Bạn cũng có thể bao gồm các thói quen dọn dẹp bổ sung shuch như quầng sáng.



1

Từ đây :

#! /bin/sh
# keepnewest
#
# Simple directory trimming tool to handle housekeeping
# Scans a directory and deletes all but the N newest files
#
# Usage: cleanup <dir> <number of files to keep>
#
# v 1.0 Piers Goodhew 1/mar/2007. No rights retained.


if [ $# -ne 2 ]; then
  echo 1>&2 "Usage: $0 <dir> <number of files to keep>"
  exit 1
fi

cd $1
files_in_dir=`ls | wc -l`
files_to_delete=`expr $files_in_dir - $2`
if [ $files_to_delete -gt 0 ]; then
  ls -t | tail -n $files_to_delete | xargs rm
  if [ $? -ne 0 ]; then
    echo "An error ocurred deleting the files"
    exit 1
  else
    echo "$files_to_delete file(s) deleted."
  fi
else
  echo "nothing to delete!"
fi

1
Cảm ơn đã cung cấp câu trả lời này. Mặc dù việc cung cấp mã như thế này rất hữu ích, nhưng nó cũng giúp cung cấp một số thông tin bổ sung về cách sử dụng mã hoặc những gì mã làm. Bạn có thể sử dụng nút chỉnh sửa để cải thiện bài viết của mình trong tương lai.
nhinkle

1

Trong Bash bạn có thể thử:

ls -1t | (i=0; while read f; do
  if [ $i -lt 10 ]; then
    ((i++))
    continue
  else
    rm -f "$f"
  fi
done)

Điều này bỏ qua 10 als mới nhất xóa phần còn lại. logrotate có thể tốt hơn, tôi chỉ muốn sửa các câu trả lời liên quan đến vỏ sai.


1

không chắc chắn điều này sẽ giúp được ai, nhưng để tránh các vấn đề tiềm ẩn với các ký tự rủng rỉnh tôi chỉ sử dụng lsđể thực hiện sắp xếp nhưng sau đó sử dụng các tệp inodeđể xóa.

Đối với thư mục hiện tại, tệp này giữ 10 tệp gần đây nhất dựa trên thời gian sửa đổi.

ls -1tri | awk '{print $1}' | head -n -10 | xargs -n1 -t -Iinode find . -inum inode -exec rm -i {} \;


0

Tôi cần một giải pháp tao nhã cho busybox (bộ định tuyến), tất cả các giải pháp xargs hoặc mảng đều vô dụng đối với tôi - không có lệnh nào như vậy có sẵn ở đó. tìm và mtime không phải là câu trả lời thích hợp vì chúng ta đang nói về 10 mục và không nhất thiết là 10 ngày. Câu trả lời của Espo là câu trả lời ngắn gọn và sạch nhất và có khả năng nhất.

Lỗi với khoảng trắng và khi không có tệp nào bị xóa, cả hai đều được giải quyết theo cách tiêu chuẩn:

rm "$(ls -td *.tar | awk 'NR>7')" 2>&-

Phiên bản giáo dục hơn một chút: Chúng tôi có thể làm tất cả nếu chúng tôi sử dụng awk khác nhau. Thông thường, tôi sử dụng phương thức này để truyền (trả về) các biến từ awk sang sh. Khi chúng ta đọc tất cả thời gian không thể thực hiện được, tôi xin khác: đây là phương pháp.

Ví dụ cho các tệp .tar không có vấn đề liên quan đến khoảng trắng trong tên tệp. Để kiểm tra, thay thế "rm" bằng "ls".

eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}')

Giải trình:

ls -td *.tarliệt kê tất cả các tập tin .tar được sắp xếp theo thời gian. Để áp dụng cho tất cả các tệp trong thư mục hiện tại, hãy xóa phần "d * .tar"

awk 'NR>7... bỏ qua 7 dòng đầu tiên

print "rm \"" $0 "\"" xây dựng một dòng: rm "tên tệp"

eval thực hiện nó

Vì chúng tôi đang sử dụng rm, tôi sẽ không sử dụng lệnh trên trong một tập lệnh! Cách sử dụng khôn ngoan là:

(cd /FolderToDeleteWithin && eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}'))

Trong trường hợp sử dụng ls -tlệnh sẽ không gây hại gì cho các ví dụ ngớ ngẩn như: touch 'foo " bar'touch 'hello * world'. Không phải là chúng ta từng tạo ra những tập tin có tên như vậy trong đời thực!

Sidenote. Nếu chúng ta muốn truyền một biến cho sh theo cách này, chúng ta chỉ cần sửa đổi bản in:

print "VarName="$1

để đặt biến VarNamethành giá trị của $1. Nhiều biến có thể được tạo trong một lần. Điều này VarNametrở thành một biến sh bình thường và có thể được sử dụng bình thường trong một tập lệnh hoặc shell sau đó. Vì vậy, để tạo các biến với awk và đưa chúng trở lại trình bao:

eval $(ls -td *.tar | awk 'NR>7 { print "VarName="$1 }'); echo "$VarName"

0

Tôi đồng ý rằng phân tích ls là xấu xa!

Đảm bảo chỉ có 5 tệp cuối cùng được giữ trong /srv/backupsđường dẫn.

find /srv/backups -maxdepth 1 -type f -printf '%Ts\t%P\n' \
    | sort -rn \
    | tail -n +6 \
    | cut -f2- \
    | xargs -r r

0

Dựa trên phản hồi của Isaac Freeman, nhưng làm việc trên bất kỳ thư mục nào:

ls -1tr $PATH_TO_DELETE/* | head -n -10 | xargs -d '\n' rm -f --

Bạn cũng có thể đặt tiền tố, như $PATH_TO_DELETE/testFi*

Nếu bạn sử dụng *sau PATH lssẽ xuất tên tệp tuyệt đối, ít nhất là trong "my" bash :)

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.