Xóa tất cả nhưng các tệp X gần đây nhất trong bash


157

Có một cách đơn giản, trong một môi trường UNIX khá chuẩn với bash, để chạy một lệnh để xóa tất cả trừ các tệp X gần đây nhất khỏi một thư mục?

Để cung cấp thêm một chút về một ví dụ cụ thể, hãy tưởng tượng một số công việc định kỳ viết ra một tệp (giả sử, tệp nhật ký hoặc bản sao lưu tar-ed) vào một thư mục mỗi giờ. Tôi muốn có một cách để chạy một công việc định kỳ khác sẽ loại bỏ các tệp cũ nhất trong thư mục đó cho đến khi có ít hơn, giả sử, 5.

Và để rõ ràng, chỉ có một tệp hiện tại, nó sẽ không bao giờ bị xóa.

Câu trả lời:


117

Các vấn đề với các câu trả lời hiện có:

  • không có khả năng xử lý tên tệp với không gian nhúng hoặc dòng mới.
    • trong trường hợp các giải pháp gọi rmtrực tiếp vào một sự thay thế lệnh không được trích dẫn ( rm `...`), sẽ có thêm một rủi ro về tình trạng toàn cầu hóa ngoài ý muốn.
  • không có khả năng phân biệt giữa các tệp và thư mục (nghĩa là, nếu các thư mục tình cờ nằm ​​trong số 5 mục hệ thống tệp được sửa đổi gần đây nhất, bạn sẽ giữ lại ít hơn 5 tệp và áp dụng rmvào thư mục sẽ thất bại).

Câu trả lời của wnoise giải quyết những vấn đề này, nhưng giải pháp là GNU -specific (và khá phức tạp).

Đây là một giải pháp thực tế, tuân thủ POSIX chỉ đi kèm với một cảnh báo : nó không thể xử lý tên tệp với dòng mới được nhúng - nhưng tôi không coi đó là mối quan tâm trong thế giới thực đối với hầu hết mọi người.

Đối với hồ sơ, đây là lời giải thích cho lý do tại sao nói chung không nên phân tích lsđầu ra: http://mywiki.wooledge.org/ParsingLs

ls -tp | grep -v '/$' | tail -n +6 | xargs -I {} rm -- {}

Ở trên là không hiệu quả , vì xargsphải gọi rmmột lần cho mỗi tên tệp.
Nền tảng của bạn xargscó thể cho phép bạn giải quyết vấn đề này:

Nếu bạn có GNU xargs , hãy sử dụng -d '\n', điều này làm cho xargsmỗi dòng đầu vào là một đối số riêng biệt, nhưng sẽ chuyển càng nhiều đối số sẽ phù hợp với một dòng lệnh cùng một lúc :

ls -tp | grep -v '/$' | tail -n +6 | xargs -d '\n' -r rm --

-r( --no-run-if-empty) đảm bảo rằng rmkhông được gọi nếu không có đầu vào.

Nếu bạn có BSD xargs (bao gồm cả trên macOS ), bạn có thể sử dụng -0để xử lý NULđầu vào được phân tách, sau khi dịch dòng mới sang ký tự NUL( 0x0), cũng chuyển (thường) tất cả tên tệp cùng một lúc (cũng sẽ hoạt động với GNU xargs):

ls -tp | grep -v '/$' | tail -n +6 | tr '\n' '\0' | xargs -0 rm --

Giải trình:

  • ls -tpin tên của các mục hệ thống tập tin được sắp xếp theo cách chúng được sửa đổi gần đây, theo thứ tự giảm dần (các mục được sửa đổi gần đây nhất trước tiên) ( -t), với các thư mục được in bằng dấu /để đánh dấu chúng như vậy ( -p).
  • grep -v '/$'sau đó loại bỏ các thư mục từ danh sách kết quả, bằng cách bỏ qua -vcác dòng có dấu /( /$).
    • Hãy cẩn thận : Vì một liên kết tượng trưng đến một thư mục về mặt kỹ thuật không phải là một thư mục, các liên kết tượng trưng đó sẽ không bị loại trừ.
  • tail -n +6bỏ qua 5 mục đầu tiên trong danh sách, thực tế sẽ trả về tất cả trừ 5 tệp được sửa đổi gần đây nhất, nếu có.
    Lưu ý rằng để loại trừ Ncác tệp, N+1phải được chuyển đến tail -n +.
  • xargs -I {} rm -- {}(và các biến thể của nó) sau đó gọi rmtrên tất cả các tệp này; nếu không có trận đấu nào cả, xargssẽ không làm gì cả.
    • xargs -I {} rm -- {}định nghĩa giữ chỗ {}đại diện cho toàn bộ mỗi dòng đầu vào , do rmđó được gọi một lần cho mỗi dòng đầu vào, nhưng với tên tệp có không gian nhúng được xử lý chính xác.
    • --trong mọi trường hợp để đảm bảo rằng bất kỳ tên tập tin điều đó xảy ra để bắt đầu với -không nhầm lẫn với tùy chọn bằng rm.

Một biến thể của vấn đề ban đầu, trong trường hợp các tệp phù hợp cần được xử lý riêng lẻ hoặc được thu thập trong một mảng shell :

# One by one, in a shell loop (POSIX-compliant):
ls -tp | grep -v '/$' | tail -n +6 | while IFS= read -r f; do echo "$f"; done

# One by one, but using a Bash process substitution (<(...), 
# so that the variables inside the `while` loop remain in scope:
while IFS= read -r f; do echo "$f"; done < <(ls -tp | grep -v '/$' | tail -n +6)

# Collecting the matches in a Bash *array*:
IFS=$'\n' read -d '' -ra files  < <(ls -tp | grep -v '/$' | tail -n +6)
printf '%s\n' "${files[@]}" # print array elements

2
Chắc chắn là tốt hơn so với hầu hết các câu trả lời khác ở đây, vì vậy tôi rất vui lòng cho vay hỗ trợ của mình, ngay cả khi tôi coi việc bỏ qua trường hợp dòng mới là một việc chỉ nên làm một cách thận trọng.
Charles Duffy

2
Nếu bạn lskhông có trong thư mục hiện tại, thì các đường dẫn đến tệp sẽ chứa '/', điều đó có nghĩa là grep -v '/'sẽ không khớp với bất cứ thứ gì. Tôi tin grep -v '/$'là những gì bạn muốn chỉ loại trừ các thư mục.
waldol1

1
@ waldol1: Cảm ơn; Tôi đã cập nhật câu trả lời để bao gồm đề xuất của bạn, điều này cũng làm cho greplệnh rõ ràng hơn về mặt khái niệm. Tuy nhiên, lưu ý rằng vấn đề bạn mô tả sẽ không xuất hiện với một đường dẫn thư mục; ví dụ, ls -p /private/varvẫn sẽ chỉ in tên tập tin. Chỉ khi bạn vượt qua nhiều đối số tệp (thường thông qua toàn cầu), bạn mới thấy các đường dẫn thực tế trong đầu ra; ví dụ: ls -p /private/var/*(và bạn cũng sẽ thấy nội dung của các thư mục con phù hợp, trừ khi bạn cũng bao gồm -d).
mkuity0

108

Xóa tất cả trừ 5 (hoặc bất kỳ số nào) của các tệp gần đây nhất trong một thư mục.

rm `ls -t | awk 'NR>5'`

2
Tôi cần điều này để chỉ xem xét các tập tin lưu trữ của tôi. đổi ls -tthànhls -td *.bz2
James T Snell

3
Tôi đã sử dụng điều này cho các thư mục bằng cách thay đổi nó thành rm -rf ls -t | awk 'NR>1'(tôi chỉ muốn gần đây nhất). Cảm ơn!
lohiagphin91

10
ls -t | awk 'NR>5' | xargs rm -f nếu bạn thích đường ống và bạn cần phải khắc phục lỗi nếu không có gì để xóa.
H2ONaCl

16
Súc tích và có thể đọc được, có lẽ, nhưng nguy hiểm khi sử dụng; nếu cố gắng xóa một tập tin được tạo bằng touch 'hello * world', điều này sẽ xóa hoàn toàn mọi thứ trong thư mục hiện tại .
Charles Duffy

1
Mặc dù điều này đã được trả lời vào năm 2008, nó hoạt động như một cơ duyên và chỉ là những gì tôi cần để xóa các bản sao lưu cũ khỏi một thư mục cụ thể. Tuyệt vời.
Rens Tillmann

86
(ls -t|head -n 5;ls)|sort|uniq -u|xargs rm

Phiên bản này hỗ trợ tên có dấu cách:

(ls -t|head -n 5;ls)|sort|uniq -u|sed -e 's,.*,"&",g'|xargs rm

20
Lệnh này sẽ không xử lý chính xác các tệp có khoảng trắng trong tên.
tylerl

5
(ls -t|head -n 5;ls)là một nhóm chỉ huy . Nó in 5 tập tin gần đây nhất hai lần. sortđặt các đường giống hệt nhau. uniq -uloại bỏ trùng lặp, sao cho tất cả trừ 5 tệp gần đây nhất vẫn còn. xargs rmkêu gọi rmtừng người trong số họ.
Fabien

15
Điều này sẽ xóa tất cả các tệp của bạn nếu bạn có 5 hoặc ít hơn! Thêm --no-run-if-emptyvào xargsnhư trong (ls -t|head -n 5;ls)|sort|uniq -u|xargs --no-run-if-empty rmxin vui lòng cập nhật câu trả lời.
Gonfi den Tschal

3
Ngay cả cái "hỗ trợ tên có dấu cách" cũng nguy hiểm. Hãy xem xét một tên có chứa các trích dẫn theo nghĩa đen: touch 'foo " bar'sẽ loại bỏ toàn bộ phần còn lại của lệnh.
Charles Duffy

2
... An toàn hơn khi sử dụng xargs -d $'\n'so với việc trích dẫn trích dẫn vào nội dung của bạn, mặc dù NUL phân định luồng đầu vào (yêu cầu sử dụng một cái gì đó ngoài lsviệc thực sự làm đúng) là lựa chọn lý tưởng.
Charles Duffy

59

Biến thể đơn giản hơn của câu trả lời của thelsdj:

ls -tr | head -n -5 | xargs --no-run-if-empty rm 

ls -tr hiển thị tất cả các tệp, đầu tiên cũ nhất (-t mới nhất trước, ngược lại -r).

head -n -5 hiển thị tất cả trừ 5 dòng cuối cùng (tức là 5 tệp mới nhất).

xargs rm gọi rm cho mỗi tệp được chọn.


15
Cần thêm --no-run-if-blank vào xargs để nó không bị lỗi khi có ít hơn 5 tệp.
Tom

l -1tr | đầu -n -5 | xargs rm <---------- bạn cần thêm -1 vào ls hoặc bạn sẽ không nhận được đầu ra danh sách để đầu hoạt động chính xác
Al Joslin

3
@AlJoslin, -1được mặc định khi đầu ra là một đường ống, vì vậy nó không bắt buộc ở đây. Điều này có vấn đề lớn hơn nhiều, liên quan đến hành vi mặc định xargskhi phân tích tên bằng dấu cách, dấu ngoặc kép, & c.
Charles Duffy

dường như --no-run-if-emptykhông được công nhận trong vỏ của tôi. Tôi đang sử dụng Cmder trên windows.
Ở lại

Có thể cần sử dụng -0tùy chọn nếu tên tệp có thể chứa khoảng trắng. Chưa được thử nghiệm mặc dù. nguồn
Keith

18
find . -maxdepth 1 -type f -printf '%T@ %p\0' | sort -r -z -n | awk 'BEGIN { RS="\0"; ORS="\0"; FS="" } NR > 5 { sub("^[0-9]*(.[0-9]*)? ", ""); print }' | xargs -0 rm -f

Yêu cầu GNU tìm for -printf và GNU sort cho -z và GNU awk cho "\ 0" và GNU xargs cho -0, nhưng xử lý các tệp có dòng mới hoặc dấu cách được nhúng.


2
Nếu bạn muốn xóa thư mục, chỉ cần thay đổi -f thành -d và thêm -r vào rm. tìm thấy . -maxdepth 1 -type d -printf '% T @% p \ 0' | sắp xếp -r -z -n | awk 'BEGIN {RS = "\ 0"; ORS = "\ 0"; FS = ""} NR> 5 {phụ ("^ [0-9] * (. [0-9] *)?", ""); in} '| xargs -0 rm -rf
alex

1
Nhìn thoáng qua, tôi ngạc nhiên về sự phức tạp (hoặc, đối với vấn đề đó, sự cần thiết) của awklogic. Tôi có thiếu một số yêu cầu bên trong câu hỏi của OP khiến nó cần thiết không?
Charles Duffy

@Charles Duffy: Sub () xóa dấu thời gian, đó là những gì được sắp xếp trên. Dấu thời gian được tạo bởi "% T @" có thể bao gồm một phần phân số. Chia tách trên không gian với FS phá vỡ các đường dẫn với các không gian nhúng. Tôi cho rằng việc loại bỏ thông qua không gian đầu tiên hoạt động, nhưng gần như khó đọc. Dấu phân cách RS và ORS không thể được đặt trên dòng lệnh, vì chúng là NUL.
wnoise

1
@wnoise, cách tiếp cận thông thường của tôi đối với điều này là chuyển thành một while read -r -d ' '; IFS= -r -d ''; do ...vòng lặp shell - lần đọc đầu tiên chấm dứt trên không gian, trong khi cách thứ hai tiếp tục đến NUL.
Charles Duffy

@Charles Duffy: Tôi luôn lúng túng với vỏ thô, có lẽ do byzantine trích dẫn mối quan tâm. Bây giờ tôi nghĩ GNU sed -z -e 's/[^ ]* //; 1,5d'là rõ ràng nhất. (hoặc có lẽ sed -n -z -e 's/[^ ]* //; 6,$p'.
wnoise

14

Tất cả những câu trả lời không thành công khi có thư mục trong thư mục hiện tại. Đây là một cái gì đó hoạt động:

find . -maxdepth 1 -type f | xargs -x ls -t | awk 'NR>5' | xargs -L1 rm

Điều này:

  1. hoạt động khi có thư mục trong thư mục hiện tại

  2. cố gắng xóa từng tệp ngay cả khi tệp trước đó không thể bị xóa (do quyền, v.v.)

  3. không an toàn khi số lượng các tập tin trong thư mục hiện hành là quá mức và xargsbình thường vít bạn đè lên ( -x)

  4. không phục vụ cho không gian trong tên tệp (có lẽ bạn đang sử dụng hệ điều hành sai?)


5
Điều gì xảy ra nếu findtrả về tên tệp nhiều hơn mức có thể được truyền trên một dòng lệnh ls -t? (Gợi ý: Bạn nhận được nhiều lần chạy ls -t, mỗi lần chỉ được sắp xếp riêng lẻ, thay vì có thứ tự sắp xếp đúng toàn cầu; do đó, câu trả lời này bị hỏng nặng khi chạy với các thư mục đủ lớn).
Charles Duffy

12
ls -tQ | tail -n+4 | xargs rm

Liệt kê tên tệp theo thời gian sửa đổi, trích dẫn từng tên tệp. Loại trừ 3 đầu tiên (3 gần đây nhất). Loại bỏ còn lại.

EDIT sau khi nhận xét hữu ích từ mkuity0 (cảm ơn!): Đã sửa lỗi đối số -n + 3 và lưu ý rằng điều này sẽ không hoạt động như mong đợi nếu tên tệp chứa dòng mới và / hoặc thư mục chứa thư mục con.


Các -Qtùy chọn dường như không tồn tại trên máy tính của tôi.
Pierre-Adrien Buisson

4
Hmm, tùy chọn này đã có trong các tiện ích lõi GNU trong ~ 20 năm, nhưng không được đề cập trong các biến thể BSD. Bạn đang trên máy mac?
Đánh dấu

Tôi thực sự. Không nghĩ rằng có sự khác biệt cho loại lệnh thực sự cơ bản này giữa các hệ thống cập nhật. Cảm ơn câu trả lời của bạn !
Pierre-Adrien Buisson

3
@Mark: ++ cho -Q. Vâng, -Qlà một phần mở rộng GNU (đây là POSIX lsđặc tả ). Một cảnh báo nhỏ (hiếm khi là một vấn đề trong thực tế): -Qmã hóa các dòng mới được nhúng trong tên tệp dưới dạng chữ \n, rmsẽ không nhận ra. Để loại trừ 3 đầu tiên , xargsđối số phải +4. Cuối cùng, một cảnh báo áp dụng cho hầu hết các câu trả lời khác: lệnh của bạn sẽ chỉ hoạt động như dự định nếu không có thư mục con trong thư mục hiện tại.
mkuity0

1
Khi không có gì để xóa, bạn có cuộc gọi xargs với --no-run-if-emptytùy chọn:ls -tQ | tail -n+4 | xargs --no-run-if-empty rm
Olivier Lecrivain

8

Bỏ qua các dòng mới là bỏ qua bảo mật và mã hóa tốt. wnoise đã có câu trả lời tốt duy nhất. Đây là một biến thể của anh ta đặt tên tệp trong một mảng $ x

while IFS= read -rd ''; do 
    x+=("${REPLY#* }"); 
done < <(find . -maxdepth 1 -printf '%T@ %p\0' | sort -r -z -n )

2
Tôi khuyên IFSbạn nên xóa - nếu không, bạn có nguy cơ mất khoảng trắng theo sau từ tên tệp. Có thể phạm vi điều đó với lệnh đọc:while IFS= read -rd ''; do
Charles Duffy

1
tại sao "${REPLY#* }"?
msciwoj

4

Nếu tên tệp không có khoảng trắng, điều này sẽ hoạt động:

ls -C1 -t| awk 'NR>5'|xargs rm

Nếu tên tệp không có khoảng trắng, đại loại như

ls -C1 -t | awk 'NR>5' | sed -e "s/^/rm '/" -e "s/$/'/" | sh

Logic cơ bản:

  • lấy danh sách các tập tin theo thứ tự thời gian, một cột
  • lấy tất cả trừ 5 đầu tiên (n = 5 cho ví dụ này)
  • phiên bản đầu tiên: gửi chúng đến rm
  • phiên bản thứ hai: tạo một tập lệnh sẽ loại bỏ chúng đúng cách

Đừng quên while readmẹo xử lý không gian: ls -C1 -t | awk 'NR>5' | while read d ; do rm -rvf "$d" ; done
pinkeen

1
@pinkeen, không hoàn toàn an toàn như được đưa ra ở đó. while IFS= read -r dsẽ tốt hơn một chút - -rngăn chặn các dấu gạch chéo ngược được sử dụng readIFS=ngăn chặn tự động cắt xén khoảng trắng.
Charles Duffy

4
BTW, nếu một người lo lắng về tên tập tin thù địch, đây là một cách tiếp cận cực kỳ nguy hiểm. Hãy xem xét một tập tin được tạo ra với touch $'hello \'$(rm -rf ~)\' world'; các trích dẫn bằng chữ bên trong tên tệp sẽ chống lại các trích dẫn bằng chữ mà bạn thêm vào sed, dẫn đến mã trong tên tệp được thực thi.
Charles Duffy

1
(để rõ ràng, "cái này" ở trên đã đề cập đến | shbiểu mẫu, đây là cái có lỗ hổng tiêm vỏ).
Charles Duffy

2

Với zsh

Giả sử bạn không quan tâm đến các thư mục hiện tại và bạn sẽ không có nhiều hơn 999 tệp (chọn số lớn hơn nếu bạn muốn hoặc tạo một vòng lặp while).

[ 6 -le `ls *(.)|wc -l` ] && rm *(.om[6,999])

Trong *(.om[6,999]), các .tệp phương tiện, osắp xếp thứ tự mphương tiện, phương tiện theo ngày sửa đổi (đặt athời gian truy cập hoặc cthay đổi inode), [6,999]chọn một phạm vi tệp, vì vậy không phải là 5 đầu tiên.


Hấp dẫn, nhưng đối với cuộc sống của tôi, tôi không thể làm cho vòng loại toàn cầu sắp xếp ( om) hoạt động (mọi cách sắp xếp mà tôi đã thử đều không có kết quả - cả trên OSX 10.11.2 (đã thử với zsh 5.0.8 và 5.1.1) , cũng như trên Ubuntu 14.04 (zsh 5.0.2)) - tôi còn thiếu gì?. Đối với điểm cuối phạm vi: không cần mã hóa cứng, chỉ cần sử dụng -1để tham khảo mục cuối cùng và do đó bao gồm tất cả các tệp còn lại : [6,-1].
mkuity0

2

Tôi nhận ra đây là một chủ đề cũ, nhưng có lẽ ai đó sẽ được hưởng lợi từ điều này. Lệnh này sẽ tìm các tệp trong thư mục hiện tại:

for F in $(find . -maxdepth 1 -type f -name "*_srv_logs_*.tar.gz" -printf '%T@ %p\n' | sort -r -z -n | tail -n+5 | awk '{ print $2; }'); do rm $F; done

Điều này mạnh hơn một chút so với một số câu trả lời trước đây vì nó cho phép giới hạn miền tìm kiếm của bạn ở các tệp khớp với biểu thức. Đầu tiên, tìm tập tin phù hợp với bất kỳ điều kiện bạn muốn. In các tệp đó với dấu thời gian bên cạnh chúng.

find . -maxdepth 1 -type f -name "*_srv_logs_*.tar.gz" -printf '%T@ %p\n'

Tiếp theo, sắp xếp chúng theo dấu thời gian:

sort -r -z -n

Sau đó, loại bỏ 4 tệp gần đây nhất khỏi danh sách:

tail -n+5

Lấy cột thứ 2 (tên tệp, không phải dấu thời gian):

awk '{ print $2; }'

Và sau đó gói toàn bộ điều đó thành một tuyên bố:

for F in $(); do rm $F; done

Đây có thể là một lệnh dài dòng hơn, nhưng tôi đã may mắn hơn nhiều khi có thể nhắm mục tiêu các tệp có điều kiện và thực hiện các lệnh phức tạp hơn đối với chúng.


1

tìm thấy cmd thú vị trong Sed-Onliners - Xóa 3 dòng cuối cùng - vì nó hoàn hảo cho một cách khác để lột da mèo (không sao) nhưng ý tưởng:

 #!/bin/bash
 # sed cmd chng #2 to value file wish to retain

 cd /opt/depot 

 ls -1 MyMintFiles*.zip > BigList
 sed -n -e :a -e '1,2!{P;N;D;};N;ba' BigList > DeList

 for i in `cat DeList` 
 do 
 echo "Deleted $i" 
 rm -f $i  
 #echo "File(s) gonzo " 
 #read junk 
 done 
 exit 0

1

Xóa tất cả trừ 10 tệp mới nhất (hầu hết là gần đây)

ls -t1 | head -n $(echo $(ls -1 | wc -l) - 10 | bc) | xargs rm

Nếu ít hơn 10 tệp, không có tệp nào bị xóa và bạn sẽ có: lỗi đầu: số dòng bất hợp pháp - 0

Để đếm tập tin với bash


1

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 (dạng đơn giản, không dung sai khoảng trắng):

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
leaveCount=5
fileCount=$(ls -1 *.log | wc -l)
tailCount=$((fileCount - leaveCount))

# avoid negative tail argument
[[ $tailCount < 0 ]] && tailCount=0

ls -t *.log | tail -$tailCount | xargs rm -f

2
xargskhông có -0hoặc ở mức tối thiểu -d $'\n'là không đáng tin cậy; quan sát cách thức này hoạt động với một tệp có khoảng trắng hoặc trích dẫn các ký tự trong tên của nó.
Charles Duffy

0

Tôi đã thực hiện điều này thành một kịch bản bash shell. Cách sử dụng: keep NUM DIRtrong đó NUM là số lượng tệp cần giữ và DIR là thư mục để xóa.

#!/bin/bash
# Keep last N files by date.
# Usage: keep NUMBER DIRECTORY
echo ""
if [ $# -lt 2 ]; then
    echo "Usage: $0 NUMFILES DIR"
    echo "Keep last N newest files."
    exit 1
fi
if [ ! -e $2 ]; then
    echo "ERROR: directory '$1' does not exist"
    exit 1
fi
if [ ! -d $2 ]; then
    echo "ERROR: '$1' is not a directory"
    exit 1
fi
pushd $2 > /dev/null
ls -tp | grep -v '/' | tail -n +"$1" | xargs -I {} rm -- {}
popd > /dev/null
echo "Done. Kept $1 most recent files in $2."
ls $2|wc -l
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.