Bash

通過刪除舊文件來保持文件夾中的文件數量不變

  • January 24, 2017

我正在嘗試創建一個腳本並每 5 分鐘在 crontab 中執行一次,以便文件夾中的文件數始終保持 50000。如果有更多,我希望腳本刪除舊文件。

#!/bin/bash
LIMIT=500000
NO=0
#Get the number of files, that has `*.pcap` in its name, with last modified time 5 days     ago

NUMBER=$(find /mnt/md0/capture/DCN/ -maxdepth 1 -name "*.pcap" |wc -l)
if [[ $NUMBER -gt $LIMIT ]]  #if number greater than limit
then
 del=$(($NUMBER-$LIMIT))
  if [ "$del" -lt "$NO" ]
   then
    del=$(($del*-1))
  fi
  echo $del
  FILES=$(
    find /mnt/md0/capture/DCN/ -maxdepth 1 -type f -name "*.pcap" -print0 |
      xargs -0 ls -lt |
      tail -$del |
      awk '{print $8}'
  )
 rm -f ${FILES[@]}
 #delete the originals

fi

它並沒有真正起作用,因為文件數量太大而無法執行。有沒有其他方法可以完成這項工作?

我執行了命令:

find /mnt/md0/capture/DCN/ -maxdepth 1 -type f -name "*.pcap" -print0 |
 xargs -0 ls -lt | tail -n "$del" | awk '{print $8}'

我觀察到的問題是awk '{print $8}'列印時間,而不是文件名。 awk '{print $9}'會解決的。

另一個問題是它xargs可能會執行ls -lt多次,這會給您一個接一個地排序的文件列表,但不會對整個列表進行排序。

但是,似乎還可以進行其他簡化。您可以通過以下方式獲取最舊的文件:

ls -dt /mnt/md0/capture/DCN/*.pcap | tail -n "$del"

正如您的文章所認為的那樣,這假設文件名中沒有空格、製表符或換行符。

因此,刪除最舊$del文件的完整命令可能是:

ls -dt /mnt/md0/capture/DCN/*.pcap | tail -n "$del" | xargs rm

**更多:**如果您的文件名可能包含空格、製表符、反斜杠或引號(但包含換行符),請使用(假設 GNU ls4.0 (1998) 或更高版本):

ls -dt --quoting-style=shell-always /mnt/md0/capture/DCN/*.pcap |
 tail -n "$del" | xargs rm

對於那些不想對文件名稱做出假設的人:

zsh

#! /bin/zsh -
keep=5000
rm -f /mnt/md0/capture/DCN/*.pcap(D.om[$((keep+1)),-1])

那是使用zsh萬用字元:

  • D: 包括隱藏文件(D ot 文件)。
  • .: 僅正常文件(如find’s -type f
  • om: 年齡倒序**(****基於**修改時間)
  • [$((keep+1)),-1]: 只包括到最後的第5001條。

(如果要刪除的文件列表非常大,它可能會失敗,在這種情況下,您可能希望使用zargs它來拆分它,或者啟用zsh’s builtin rmwith zmodload zsh/files)。

使用相對較新版本的 GNU 工具:

cd /mnt/md0/capture/DCN/ &&
 find . -maxdepth 1 -name '*.pcap' -type f -printf '%T@@%p\0' |
   sort -zrn | sed -z "s/[^@]*@//;1,$keep d" | xargs -r0 rm -f

(假設 GNU sed 4.2.2 或更高版本(2012)-z,GNU sort1.14 或更高版本(1996)-z

find建構一個 NUL 分隔的文件名列表,前面帶有 Unix 時間戳(如1390682991.0859627500@./file),按sort. sed刪除時間戳並僅從第 5001條記錄中列印。這作為參數傳遞給rmusing xargs -r0

或(使用任何版本的 GNU 工具):

cd /mnt/md0/capture/DCN/ &&
 find . -maxdepth 1 -name '*.pcap' -type f -printf '%T@@%p\0' |
   tr '\0\n' '\n\0' | sort -rn | tail -n "+$(($keep+1))" |
   cut -d @ -f2- | tr  '\0\n' '\n\0' | xargs -r0 rm -f

相同,除了我們cut用於刪除時間戳並tail選擇從 5001 開始的行。由於 GNUcut並且tail不支持-z處理 NUL 分隔的記錄,我們使用tr在饋送之前和之後交換換行符和 NUL 字元數據給他們。

使用 GNU ls(4.0 (1998) 或更高版本),並且bash

shopt -s dotglob
cd /mnt/md0/capture/DCN/ &&
 eval "files=($(ls -dt --quoting-style=shell-always -- *.pcap))" &&
 rm -f -- "${files[@]:$keep}"

(如果文件列表很大,這也可能會失敗。另請注意,它可能包括非正常的 pcap 文件(否-type f))。

標準/POSIXly/便攜,這要棘手得多:

cd /mnt/md0/capture/DCN/ &&
 ls -dt ./.pcap ./.*.pcap ./*.pcap | awk -v keep="$keep" '
   function process() {
     if (++n > keep) {
       gsub(/[ \t\n"\\'\'']/,"\\\\&", file)
       print file
       file = ""
     }
   }
   /\// {
     if (NR > 1) process()
     file=$0
     next
   }
   {file = file "\n" $0}
   END {if (NR > 0) process()}' | xargs rm -f

(同樣,您可能會達到參數數量的限制,並且它不會檢查正常文件)。

棘手的一點是用換行符處理文件名。上面,我們傳遞./*給 每個文件名將包含一次的方法,我們使用它ls來辨識每個文件名從哪一行開始,然後我們知道哪個換行符(除了所有其他特殊的換行符)到逃跑。/``awk``xargs``xargs

引用自:https://unix.stackexchange.com/questions/111819