web-dev-qa-db-ja.com

ディレクトリ内のファイル数を数え、数が5を超えた場合、最も古いファイルを削除するにはどうすればよいですか?

CentOS 7サーバーで自動バックアップを実行するスクリプトを作成しました。バックアップは/ home/backupディレクトリに保存されます。スクリプトは機能しますが、バックアップが発生した後にファイルをカウントする方法を組み込み、数が5を超える場合は、最も古いバックアップを削除します。

以下は、バックアップスクリプト用のものです。

#!/bin/bash

#mysqldump variables

FILE=/home/backup/databasebk_!`date +"Y-%m-%d_%H:%M"`.sql
DATABASE=database
USER=root
PASS=my password

#backup command process

mysqldump --opt --user=${USER} --password=${PASS} ${DATABASE} > ${FILE}

#zipping the backup file

gzip $FILE

#send message to the user with the results

echo "${FILE}.gz was created:"
ls -l ${FILE}.gz

# This is where I would like to count the number of files 
# in the directory and if there are more than 5 I would like
# to delete the oldest file. Any help is greatly appreciated

ありがとう-マイク

14
Mike

set -- /home/backup/databasebk_*を見て、$#が5より大きい場合、ファイルを削除します。

したがって、コードは次のようになります

set -- /home/backup/databasebk_*
while [ $# -gt 5 ]
do
  echo "Removing old backup $1"
  rm "$1"
  shift
done

これは、選択したファイル名が自動的に「最も古いものから」の順序になるため機能します。

一貫性を保つために変数を設定します(通常はBASEと呼びますが、好きなように呼び出すことができます)

そう

BASE=/home/backup/databasebk_
FILE=${BASE}!`date +"%Y-%m-%d_%H:%M"`.sql
....
set -- ${BASE}*
while [ $# -gt 5 ]
do
  echo "Removing old backup $1"
  rm "$1"
  shift
done
10
Stephen Harris

ループのない純粋なBashでは:

ls -t | tail -n +6 | xargs -d '\n' rm

説明:

  • ls -tは、現在のディレクトリにあるすべてのファイルを、変更時刻でソートされた新しいものから最初に出力します。
  • tail -n +6は最初の5行を無視し、6行目以降を出力します。
  • xargs -d '\n' rmは、渡されたファイルを1行に1つずつ削除します。ファイル名にスペースや引用符がないことが確実である場合は、xargs rmだけを使用できます。

これは、dirに5つのファイルのみを残すために必要な数のファイルを削除することに注意してください。最も古いファイルを1つだけ削除する場合は、+61に置き換えます。

19
BoppreH

このアプリケーションはスクリプトで達成しようとしているアクションをすでに実行しているため、より良いアプローチはlogrotateを使用することだと思います。テストサーバーで次のスクリプトをテストしました。

  1. データベースが手動でバックアップされます。ファイルが存在しないとlogrotateが実行されないため、これが必要です。
    $ mysqldump -uroot -pmy5trongpass database > mydatabase.sql
    $ ls
    $ mydatabase.sql

最初のデータベースが作成されなかった場合のエラーの例。

    #**logrotate -f** will force our new rule to run now and backup the database
    #this is for testing purposes.

    $ logrotate -f /etc/logrotate.d/mysql_backup
    error: stat of /home/backups/mydatabase.sql failed: No such file or directory
  1. Logrotateファイル/ルールを作成します。これは、/ etc/logrotate.d /にある他のルールに基づいて作成したものです。
    $ cat /etc/logrotate.d/mysql_backup
    /home/backups/mydatabase.sql{
            create 640 root mysql
           daily
            rotate 2
            nocompress
        postrotate
            mysqldump -uroot -pmy5trongpass test > mydatabase.sql;
            echo "done"
        endscript
    }
  1. を使用して構成をテストする
$ logrotate -f /etc/logrotate.d/mysql_backup
$ logrotate -f /etc/logrotate.d/mysql_backup
done
$ ll
total 16
-rw-r-----. 1 root mysql 1261 Feb  3 21:46 mydatabase.sql
-rw-r-----. 1 root mysql 1261 Feb  3 21:44 mydatabase.sql.1
-rw-r-----. 1 root mysql 1261 Feb  3 21:44 mydatabase.sql.2

新しいファイルが作成され、その後に番号が表示されます。これは、次のオプションを使用して日付を表示するように変更できます

https://linux.die.net/man/8/logrotate

dateext
Archive old versions of log files adding a daily extension like YYYYMMDD instead of simply adding a number. The extension may be configured using the dateformat option.

dateformat format_string
Specify the extension for dateext using the notation similar to strftime(3) function. Only %Y %m %d and %s specifiers are allowed. The default value is -%Y%m%d. Note that also the character separating log name from the extension is part of the dateformat string. The system clock must be set past Sep 9th 2001 for %s to work correctly. Note that the datestamps generated by this format must be lexically sortable (i.e., first the year, then the month then the day. e.g., 2001/12/01 is ok, but 01/12/2001 is not, since 01/11/2002 would sort lower while it is later). This is because when using the rotate option, logrotate sorts all rotated filenames to find out which logfiles are older and should be removed.
11
Heysus Escobar

zshシェルの使用:

_rm -f /home/backup/databasebk_*.sql.gz(.om[6,-1])
_

これにより、_/home/backup/databasebk_*.sql.gz_に一致する通常のファイルのパス名のリストが作成され、ファイルの変更タイムスタンプでリストがソートされ(最近変更されたものが最初に)、要素6から最後まで抽出されます。これらのファイルは、削除のために_rm -f_に渡されます。

glob修飾子、_(.om[6,-1])_、通常のファイルを指定するファイル名展開パターンの最後にあります(_._)、変更時刻による順序付けom)、およびそれ6番目から最後の一致までが返されます(_[6,-1]_)。

6
Kusalananda
files=( /home/backup/* )
# files is now an array containing file names in the directory

oldest=""
if (( ${#files[@]} > 5 ))  ## are there more than five?
then 
  for name in "${files[@]}" ## loop to find oldest
  do
    a=$( stat --printf=%Y "$name" )
    if  [ -z "$oldest" ] || (( a < age ))
    then 
       age="$a"
       oldest="$name"
    fi
  done
  rm "$oldest"
fi

いくつかのコメント:

私は言う /home/backup/*これは、そのディレクトリ内のすべてのファイルを検索します/home/backup/databasebk_*.sql.gz代わりに、圧縮されたダンプを見つけるだけ

日付がYMDhm形式の順序になっているため、ファイルシステムに従って最も古いファイルを見つけるために「stat」を使用しています。代わりにファイル名を比較できます。

 if  [ -z "$oldest" ] || [ "$name" -lt "$oldest" ]

/ home/backup /にサブディレクトリがあると、このスクリプトが壊れます

2
Jasen

始める前に、私は他の答えを最も高く評価しており、それらはプロセスの十分な理解を反映していると言いたいと思います。

私は今、あなたの立場に立って、簡単なものを探して始め、それを土台にしていきます。最初に問題を説明しましょう。

  1. ファイル数が5を超えているかどうかを確認します。
  2. 5以下の場合は何もしません。
  3. 5を超える場合は、5より古いファイルを見つけて削除します(1ファイルのみ)。

これが私のアプローチです:

  1. ls -1 |wc -lを使用して、ファイルの数をカウントします。
  2. Ifステートメントを使用して、リストの数が5より大きいかどうかを確認します。
  3. 5より大きい場合は、適切なオプションを指定してfindコマンドを使用し、5日より古いものを削除します。

これがコードです:

if [[ $(ls -1|wc -l) -gt 5 ]]; then 

  ls -1t |tail -n1 |xargs -n1 rm -f  

fi

1行目の説明:そのディレクトリ内のファイル数が5を超えているかどうかを確認します。

2行目の説明:ファイル数が5を超えた場合にのみ実行されます(4つのファイルがあり、そのうちの1つが100日前の日付の場合、実行されません)。それらは日付でソートされ、tailコマンドを実行して最も古いもの(リストの最後のもの)を取得します。次に、そのファイル名をxargsコマンドに渡し、rm -f(強制)を使用してファイルを削除します。

解決策を提供してくれた他のすべての人に感謝します!これが機能しない場合があることは知っています(6つを超えるファイル、同じ日付の2つのファイルなど)。しかし、Mikeは新しいので、シンプルに保つように努めました:)

1
Hopping Bunny

作成rm-except-last.sh

#!/bin/bash -

set -o nounset

if ! (($#)); then
  echo Usage: "$BASH_SOURCE" N_NOT_TO_DELETE FILES_TO_DELETE...
  exit 1
fi

declare N="${1:?Missing N_NOT_TO_DELETE.}"
if ! [[ "$N" =~ ^[0-9]+$ ]]; then
  echo 1>&2 -E Not a number: "$N"
  exit 1
fi
shift

declare -a roll=()
while ((${#roll[@]} < N)); do
  roll+=('')
done

if ! ((N)); then
  rm -rfv -- "$@"
  exit $?
fi

declare n=0 file=''
for file in "$@"; do
  declare rmfile="${roll[n]}"
  [ -n "$rmfile" ] && rm -rfv -- "$rmfile"
  roll[n]="$file"
  ((n = (n + 1) % N))
done

次に:

$ rm-except-last.sh 5 /home/backup/databasebk_*.sql

1
Luchostein