web-dev-qa-db-ja.com

bashスクリプトの最新の3つを除くすべてのファイルを削除します

質問:ディレクトリ内の最新の3つを除くすべてのファイルをどのように削除しますか?

最新の3つのファイルを見つけるのは簡単です。

ls -t | head -3

しかし、最新の3つのファイルを除くすべてのファイルを見つける必要があります。それを行うにはどうすればよいですか?また、そのために不要なforループを作成せずに、同じ行でこれらのファイルを削除するにはどうすればよいですか?

私はこれにDebian Wheezyとbashスクリプトを使用しています。

20
bytecode77

これにより、最新の3つを除くすべてのファイルがリストされます。

ls -t | tail -n +4

これはそれらのファイルを削除します:

ls -t | tail -n +4 | xargs rm --

これはドットファイルもリストします:

ls -At | tail -n +4

とドットファイルで削除:

ls -At | tail -n +4 | xargs rm --

ただし、ファイル名に改行やスペースなどの面白い文字が含まれている場合、lsの解析は危険な場合があります。ファイル名に変な文字が含まれていないことが確実な場合、lsの解析は非常に安全です。1回限りのスクリプトであればなおさらです。

繰り返し使用するスクリプトを開発している場合は、lsの出力を解析せず、ここで説明されているメソッドを使用しないでください。 http://mywiki.wooledge.org/ParsingLs

40
lesmana

以下は少し複雑に見えますが、異常な、または故意に悪意のあるファイル名であっても、修正するのに非常に注意が必要です。残念ながら、GNUツールが必要です:

count=0
while IFS= read -r -d ' ' && IFS= read -r -d '' filename; do
  (( ++count > 3 )) && printf '%s\0' "$filename"
done < <(find . -maxdepth 1 -type f -printf '%T@ %P\0' | sort -g -z) \
     | xargs -0 rm -f --

これがどのように機能するかを説明する:

  • 検索は<mtime> <filename><NUL>現在のディレクトリ内の各ファイル。
  • sort -g -zは、最初の列(時間)に基づいて行をNULで区切って、一般的な(整数ではなく浮動小数点)数値ソートを行います。
  • readループの最初のwhileは、mtimeを取り除きます(sortの実行後は不要)。
  • readループの2番目のwhileは、ファイル名を読み取ります(NULまで実行)。
  • ループはカウンターをインクリメントしてからチェックします。カウンターの状態が最初のスキップを過ぎたことを示している場合は、NULで区切られたファイル名を出力します。
  • xargs -0は次に、そのファイル名をrmを呼び出すために収集しているargvリストに追加します。
9
Charles Duffy
ls -t | tail -n +4 | xargs -I {} rm {}

1ライナーが必要な場合

6
Michael Ballent

「ls」(奇妙な名前のファイル)の問題のない解決策

これは、cevingとanubhavaの答えの組み合わせです。どちらのソリューションもうまくいきません。アーカイブ内のファイルをバックアップするために毎日実行するスクリプトを探していたので、lsの問題を回避したいと思いました(誰かがおかしな名前のファイルをバックアップフォルダーに保存した可能性があります)。だから私は私のニーズに合うように上記のソリューションを変更しました。 Cevingのソリューションは、3つの最新のファイルを削除します-私が必要として求められたものではありません。

私のソリューションはすべてのファイルを削除しますexcept 3つの最新のファイル。

find . -type f -printf '%T@\t%p\n' |
sort -t $'\t' -g | 
head -n -3 | 
cut -d $'\t' -f 2- |
xargs rm

いくつかの説明:

findは、現在のフォルダー内のすべてのファイル(ディレクトリではない)をリストします。それらはタイムスタンプとともに出力されます。
sortは、タイムスタンプに基づいて行をソートします(一番古いものから順に)。
headは、最上行から最後の3行までを出力します。
cutはタイムスタンプを削除します。
xargsは、選択したすべてのファイルに対してrmを実行します。

あなたが私の解決策を検証するために:

(
touch -d "6 days ago" test_6_days_old
touch -d "7 days ago" test_7_days_old
touch -d "8 days ago" test_8_days_old
touch -d "9 days ago" test_9_days_old
touch -d "10 days ago" test_10_days_old
)

これにより、現在のフォルダーにタイムスタンプの異なる5つのファイルが作成されます。最初にこのスクリプトを実行してから、古いファイルを削除するためのコードを実行します。

6
flohall

Zshの場合:

rm /files/to/delete/*(Om[1,-4])

dotfilesを含める場合は、括弧で囲まれた部分を(Om[1,-4]D)に置き換えます。

これはファイル名の任意の文字で正しく動作すると思います(改行でチェックするだけです)。

説明:括弧にはグロブ修飾子が含まれています。 Oは「並べ替え、降順」を意味し、mはmtimeを意味します(他の並べ替えキーについてはman zshexpnを参照-大規模なマンページ、「並べ替え」を検索)。 [1,-4]は、1から始まるインデックス1から(最後+ 1-4)までの一致のみを返します(3以外のすべてを削除する場合は-4に注意してください)。

3
FunctorSalad

ls -tは、空白や特殊なグロブ文字を含む可能性のあるファイル名には安全でないため、使用しないでください。

すべてのgnuベースのユーティリティを使用してこれを行うと、現在のディレクトリ内の最新の3つを除くすべてのファイルを削除できます。

find . -maxdepth 1 -type f -printf '%T@\t%p\0' |
sort -z -nrk1 |
tail -z -n +4 |
cut -z -f2- |
xargs -0 rm -f --
2
anubhava
ls -t | tail -n +4 | xargs -I {} rm {}

マイケルバレントの答えは、

ls -t | tail -n +4 | xargs rm --

ファイルが3つ未満の場合、エラーをスローします

1
klesk44

これは、findではなくlsSchwartzian変換 とともに使用します。

find . -type f -printf '%T@\t%p\n' |
sort -t $'\t' -g |
tail -3 |
cut -d $'\t' -f 2-

findはファイルを検索し、タイムスタンプでそれらを装飾し、表を使用して2つの値を分離します。 sortは、入力をタブで分割し、浮動小数点数を正しくソートする一般的な数値ソートを実行します。 tailは明白で、cutは装飾されていないはずです。

装飾に関する一般的な問題は、入力の一部ではない適切な区切り文字であるファイル名を見つけることです。この answer はNULL文字を使用します。

0
ceving

Flohailによる回答の拡張として。最新の3つのフォルダー以外のすべてのフォルダーを削除する場合は、以下を使用します。

find . -maxdepth 1 -mindepth 1 -type d -printf '%T@\t%p\n' |
 sort -t $'\t' -g | 
 head -n -3 | 
 cut -d $'\t' -f 2- |
 xargs rm -rf

-mindepth 1は親フォルダーを無視し、-maxdepth 1サブフォルダ。

0
tutak