web-dev-qa-db-ja.com

特定のディレクトリで最も古いディレクトリを削除するにはどうすればよいですか?

可能性のある複製:
最も古いファイルを移動するためのシェルスクリプト?

バックアップが必要なx他のディレクトリを格納するバックアップディレクトリがあります。別のディレクトリがバックアップに移動する前に実行するものが必要です。ディレクトリの数がxに達したかどうかを確認し、達した場合は、最も古いディレクトリを削除します。

bashスクリプトで実行する必要があります。

9
AAaa

lsの出力の解析は 信頼できない です。

代わりに、findを使用してディレクトリを検索し、sortを使用してタイムスタンプ順にディレクトリを並べ替えます。例えば:

_IFS= read -r -d $'\0' line < <(find . -maxdepth 1 -type d -printf '%T@ %p\0' \ 
    2>/dev/null | sort -z -n)
file="${line#* }"
# do something with $file here
_

これは何をしているのですか?

最初に、findコマンドは、現在のディレクトリ(_._)のすべてのディレクトリを検索しますが、現在のディレクトリ(_-maxdepth 1_)のサブディレクトリは検索しません。次に出力します。

  • タイムスタンプ
  • 空間
  • ファイルへの相対パス
  • NULL文字

タイムスタンプは重要です。 _%T@_の_-printf_形式指定子は、ファイルの「最終変更時刻」(mtime)を示すTと、小数秒を含む「1970年からの秒数」を示す_@_に分類されます。 。

スペースは単なる任意の区切り文字です。ファイルへのフルパスは後で参照できるようにするためのものであり、NULL文字はファイル名に含まれる不正な文字であるためターミネーターであり、そのため、パスへのパスの最後に到達したことを確実に知らせますファイル。

_2>/dev/null_を含めて、ユーザーがアクセス許可を持っていないファイルは除外しましたが、除外されているというエラーメッセージは表示されません。

findコマンドの結果は、現在のディレクトリ内のすべてのディレクトリのリストです。リストはsortにパイプで渡され、次のように指示されます。

  • _-z_ NULLを改行ではなく行終了文字として扱います。
  • _-n_数値順に並べ替え

Seconds-since-1970は常に上がるので、タイムスタンプが最も小さい番号のファイルが必要です。 sortの最初の結果は、最も小さい番号のタイムスタンプを含む行になります。あとは、ファイル名を抽出するだけです。

findsortパイプラインの結果は プロセス置換 を介してreadに渡され、そこでstdin上のファイルであるかのように読み取られます。

readのコンテキストでは、IFS変数に何も設定しません。これは、空白が区切り文字として不適切に解釈されないことを意味します。 readには、エスケープ拡張を無効にする_-r_と、行末の区切り文字をNULLにする_-d $'\0'_が伝えられ、findsortパイプラインからの出力と一致します。

タイムスタンプとスペースが前に付いた最も古いディレクトリパスを表すデータの最初のチャンクは、変数lineに読み込まれます。次に、 パラメータ置換 を式_#*_で使用します。これは、文字列の先頭から最初のスペースまでのすべての文字を、スペースを含めて何も置き換えないだけです。これにより、変更タイムスタンプが取り除かれ、ファイルへの完全なパスのみが残ります。

この時点で、ファイル名は_$file_に保存されており、_rm -rf "$file"_を含め、ファイル名を使用して好きなことができます。

より簡単な方法はありませんか?

いいえ。より単純な方法はバグがあります。

_ls -t_を使用し、tailにパイプすると、ファイル名に改行が含まれるファイルで中断されます。 rm $(anything)の場合、名前に空白が含まれるファイルは破損の原因になります。 rm "$(anything)"の場合、名前の末尾に改行があるファイルは破損の原因になります。

おそらく、特定のケースでは、より単純な方法で十分であることが確実にわかっていますが、そうすることを避けることができるのであれば、スクリプトにそのような仮定を記述しないでください。

編集する

_#!/usr/bin/env bash

dir="$1"
min_dirs=3

[[ $(find "$dir" -maxdepth 1 -type d | wc -l) -ge $min_dirs ]] &&
IFS= read -r -d $'\0' line < <(find "$dir" -maxdepth 1 -printf '%T@ %p\0' 2>/dev/null | sort -z -n)
file="${line#* }"
ls -lLd "$file"
_

最初にdirカウントをチェックするため、問題に対するより完全なソリューション。

20
Sorpigal

あなたは次のようなものを使うことができます:

#!/bin/sh    
keep=3

while [ `ls -1 | wc -l` -gt $keep ]; do
    oldest=`ls -c1 | head -1`
    echo "remove $oldest"
    rm -rf $oldest
done

find . -type d -maxdepth 1の代わりにlsを使用します。これは、ディレクトリに使用する命名スキーマによって異なります。それらが名前で自然に正しくソートされている場合は、findsortおよびheadまたはtailを使用して、最も古い/最新のディレクトリを取得できます。 lsアプローチは、ctime属性を使用してソートします。

2
armin walland