紹介する

紹介する

私は次のパスを持っています:

/dir1/dir2/

このパスには、さまざまな(関連していない)アプリケーションDetriusを含む次のディレクトリがあります。

follower1234  1-Dec-2018
follower3456  2-Dec-2018
follower4567  3-Dec-2018
follower7890  9-Jan-2019
follower8901 10-Jan-2019
leader8765    4-Dec-2018
bystander6789 5-Dec-2018

今日が2019年1月10日であるとします。

followerXXXXleaderXXXXおよびディレクトリがいくらでも存在できるとしますbystanderXXXX

私が望むのは、2週間を超えるfollowerXXXX最新のディレクトリを除くすべてのディレクトリを削除することです。followerXXX

これで、すべてのディレクトリを削除できます。特定の日付以前。しかし、それは私の問題ではありません。 2つの追加パラメータを追加します。

この場合、以下を削除したいと思います。

follower1234  1-Dec-2018
follower3456  2-Dec-2018
follower4567  3-Dec-2018

しかし、

follower7890  9-Jan-2019
follower8901 10-Jan-2019
leader8765    4-Dec-2018
bystander6789 5-Dec-2018

つまり、ファイルを削除したいのです。

(a) 一致パターン

(b) 2週間以上

(c)はパターンに一致する最新のディレクトリではありません(つまり、最後のディレクトリを維持してください)。

私の質問は次のとおりです1つのディレクトリから2週間を超えるすべてのディレクトリを削除するにはどうすればよいですか(ファイルパターンに一致する最新のディレクトリを除く)。

答え1

紹介する

質問が修正されました。

  • 私の最初の選択肢(oneliner)は新しい仕様と一致しませんでしたが、十分に古い(14日以上)ディレクトリから最新のディレクトリを保存しました。

  • 私は2番目の選択肢(シェルスクリプト)を作成しました。

    @ 1970年1月1日00:00 GMT以降の秒数(小数部を含む)。

    seclimソートされたディレクトリのリストから「秒制限」のタイムスタンプを取得するには、14日に対応する秒数を減算します。

1. 断線

以前の答えはきれいできれいですが、最新のディレクトリを維持しませんfollower。次のコマンドラインはこれを行います(スペースを含む名前を管理しますが、改行を持つ名前には問題が発生します)。

find . -type d -name "follower*" -printf "%T+ %p\n"|sort|head -n -1 | cut -d ' ' -f 2- | sed -e 's/^/"/' -e 's/$/"/' | xargs echo rm -r

このディレクトリ構造をテストするには

$ find -printf "%T+ %p\n"|sort
2019-01-10+13:11:40.6279621810 ./follower1
2019-01-10+13:11:40.6279621810 ./follower1/2/3
2019-01-10+13:11:40.6279621810 ./follower1/2/dirnam with spaces
2019-01-10+13:11:40.6279621810 ./follower1/2/name with spaces
2019-01-10+13:11:56.5968732640 ./follower1/2/file
2019-01-10+13:13:18.3975675510 ./follower2
2019-01-10+13:13:19.4016254340 ./follower3
2019-01-10+13:13:20.4056833250 ./follower4
2019-01-10+13:13:21.4097412230 ./follower5
2019-01-10+13:13:22.4137991260 ./follower6
2019-01-10+13:13:23.4138568040 ./follower7
2019-01-10+13:13:24.4219149500 ./follower8
2019-01-10+13:13:25.4259728780 ./follower9
2019-01-10+13:15:34.4094596830 ./leader1
2019-01-10+13:15:36.8336011960 .
2019-01-10+13:15:36.8336011960 ./leader2
2019-01-10+13:25:03.0751878450 ./follower1/2

このように、

$ find . -type d -name "follower*" -printf "%T+ %p\n"|sort|head -n -1 | cut -d ' ' -f 2- | sed -e 's/^/"/' -e 's/$/"/' | xargs echo rm -r
rm -r ./follower1 ./follower2 ./follower3 ./follower4 ./follower5 ./follower6 ./follower7 ./follower8

したがって、最新のディレクトリ(名前が起動しない(そしてゲームにないディレクトリ))follower9なので除外されます。followerfollowerleader1leader22

次に、タイムスタンダードを追加し、正しく機能して-mtime +14いることを確認するために別の「テスト実行」を実行します。follower物理ディレクトリが存在する場所にディレクトリを変更すると、

find . -type d -name "follower*" -mtime +14 -printf "%T+ %p\n"|sort|head -n -1 | cut -d ' ' -f 2- | sed -e 's/^/"/' -e 's/$/"/' | xargs echo rm -r

echo最後に、私たちはあなたがしたいことをするコマンドラインを削除して持っていました。

find . -type d -name "follower*" -mtime +14 -printf "%T+ %p\n"|sort|head -n -1 | cut -d ' ' -f 2- | sed -e 's/^/"/' -e 's/$/"/' | xargs rm -r

  • findfollower名前がで始まり、14日前から変更されていない現在のディレクトリのディレクトリ。
  • 印刷して分類した後head -n -1最新のfollowerディレクトリを除外
  • タイムスタンプを削除し、各ディレクトリ名の先頭と末尾に二重引用符を追加します。
  • 最後に削除したいディレクトリを削除するために、結果xargsはパイプを介してパラメータに渡されます。rm -r

2. シェルスクリプト

私は2番目の選択肢(シェルスクリプト)を作成しました。

@      seconds since Jan. 1, 1970, 00:00 GMT, with fractional part.

また、2つのオプションがあります。

  • -n試運転
  • -v言葉が多い

  • OPの要求に応じてシェルスクリプトを修正しました。モードを引数として入力「follower*」のような一重引用符内。

  • prune-dirs今より一般的なので、シェルスクリプト名を使用することをお勧めします(もはやディレクトリのクリーンアップprune-followersにのみ使用されるわけではありませんfollower*)。

実行する操作を「確認」するには、最初にこれら2つのオプションを使用してシェルスクリプトを実行し、正しく表示される場合は、シェルスクリプトが削除するのに十分な古い-nディレクトリを削除することをお勧めします。それではこれを呼び出しprune-dirsて実行可能にしましょう。

#!/bin/bash

# date        sign     comment
# 2019-01-11  sudodus  version 1.1
# 2019-01-11  sudodus  enter the pattern as a parameter
# 2019-01-11  sudodus  add usage
# 2019-01-14  sudodus  version 1.2
# 2019-01-14  sudodus  check if any parameter to the command to be performed

# Usage

usage () {
 echo "Remove directories found via the pattern (older than 'datint')

 Usage:    $0 [options] <pattern>
Examples: $0 'follower*'
          $0 -v -n 'follower*'  # 'verbose' and 'dry run'
The 'single quotes' around the pattern are important to avoid that the shell expands
the wild card (for example the star, '*') before it reaches the shellscript"
 exit
}

# Manage options and parameters

verbose=false
dryrun=false
for i in in "$@"
do
 if [ "$1" == "-v" ]
 then
  verbose=true
  shift
 elif [ "$1" == "-n" ]
 then
  dryrun=true
  shift
 fi
done
if [ $# -eq 1 ]
then
 pattern="$1"
else
 usage
fi

# Command to be performed on the selected directories

cmd () {
 echo rm -r "$@"
}

# Pattern to search for and limit between directories to remove and keep

#pattern='follower*'
datint=14  # days

tmpdir=$(mktemp -d)
tmpfil1="$tmpdir"/fil1
tmpfil2="$tmpdir"/fil2

secint=$((60*60*24*datint))
seclim=$(date '+%s')
seclim=$((seclim - secint))
printf "%s limit-in-seconds\n" $seclim > "$tmpfil1"

if $verbose
then
 echo "----------------- excluding newest match:"
 find . -type d -name "$pattern" -printf "%T@ %p\n" | sort |tail -n1 | cut -d ' ' -f 2- | sed -e 's/^/"/' -e 's/$/"/'
fi

# exclude the newest match with 'head -n -1'

find . -type d -name "$pattern" -printf "%T@ %p\n" | sort |head -n -1 >> "$tmpfil1"

# put 'limit-in-seconds' in the correct place in the sorted list and remove the timestamps

sort "$tmpfil1" | cut -d ' ' -f 2- | sed -e 's/^/"/' -e 's/$/"/' > "$tmpfil2"

if $verbose
then
 echo "----------------- listing matches with 'limit-in-seconds' in the sorted list:"
 cat "$tmpfil2"
 echo "-----------------"
fi

# create 'remove task' for the directories older than 'limit-in-seconds'

params=
while read filnam
do
 if [ "${filnam/limit-in-seconds}" != "$filnam" ]
 then
  break
 else
  params="$params $filnam"
 fi
done < "$tmpfil2"
cmd $params > "$tmpfil1"
cat  "$tmpfil1"

if ! $dryrun && ! test -z "$params"
then
 bash "$tmpfil1"
fi
rm -r $tmpdir
  • follower現在のディレクトリをサブディレクトリを含むディレクトリに変更します。
  • ファイルの作成prune-dirs
  • 実行可能にする
  • 2つのオプションで実行-v -n

    cd directory-with-subdirectories-to-be-pruned/
    nano prune-dirs  # copy and paste into the editor and save the file
    chmod +x prune-dirs
    ./prune-dirs -v -n
    

テスト

prune-dirs以下のように、次のサブディレクトリがあるディレクトリでテストしました。find

$ find . -type d -printf "%T+ %p\n"|sort
2018-12-01+02:03:04.0000000000 ./follower1234
2018-12-02+03:04:05.0000000000 ./follower3456
2018-12-03+04:05:06.0000000000 ./follower4567
2018-12-04+05:06:07.0000000000 ./leader8765
2018-12-05+06:07:08.0000000000 ./bystander6789
2018-12-06+07:08:09.0000000000 ./follower with spaces old
2019-01-09+10:11:12.0000000000 ./follower7890
2019-01-10+11:12:13.0000000000 ./follower8901
2019-01-10+13:15:34.4094596830 ./leader1
2019-01-10+13:15:36.8336011960 ./leader2
2019-01-10+14:08:36.2606738580 ./2
2019-01-10+14:08:36.2606738580 ./2/follower with spaces
2019-01-10+17:33:01.7615641290 ./follower with spaces new
2019-01-10+19:47:19.6519169270 .

使用法

$ ./prune-dirs
Remove directories found via the pattern (older than 'datint')

 Usage:    ./prune-dirs [options] <pattern>
Examples: ./prune-dirs 'follower*'
          ./prune-dirs -v -n 'follower*'  # 'verbose' and 'dry run'
The 'single quotes' around the pattern are important to avoid that the shell expands
the wild card (for example the star, '*') before it reaches the shellscript

実行-v -n(詳細なテスト実行)

$ ./prune-dirs -v -n 'follower*'
----------------- excluding newest match:
"./follower with spaces new"
----------------- listing matches with 'limit-in-seconds' in the sorted list:
"./follower1234"
"./follower3456"
"./follower4567"
"./follower with spaces old"
"limit-in-seconds"
"./follower7890"
"./follower8901"
"./2/follower with spaces"
-----------------
rm -r "./follower1234" "./follower3456" "./follower4567" "./follower with spaces old"

より一般的なパターンを使用した詳細な練習

$ LANG=C ./prune-dirs -v -n '*er*'
----------------- excluding newest match:
"./follower with spaces new"
----------------- listing matches with 'limit-in-seconds' in the sorted list:
"./follower1234"
"./follower3456"
"./follower4567"
"./leader8765"
"./bystander6789"
"./follower with spaces old"
"limit-in-seconds"
"./follower7890"
"./follower8901"
"./leader1"
"./leader2"
"./2/follower with spaces"
-----------------
rm -r "./follower1234" "./follower3456" "./follower4567" "./leader8765" "./bystander6789" "./follower with spaces old"

オプションなしで実行(ディレクトリ削除の実際のケース)

$ ./prune-dirs 'follower*'
rm -r "./follower1234" "./follower3456" "./follower4567" "./follower with spaces old"

-v「再試行」を実行

$ LANG=C ./prune-dirs -v 'follower*'
----------------- excluding newest match:
"./follower with spaces new"
----------------- listing matches with 'limit-in-seconds' in the sorted list:
"limit-in-seconds"
"./follower7890"
"./follower8901"
"./2/follower with spaces"
-----------------
rm -r

シェルスクリプトは「秒制限の上」ディレクトリを一覧表示せず、rm -rコマンドラインのファイルを一覧表示しないため、操作は完了します(正しい結果です)。ただし、数日後にシェルスクリプトを再実行すると、一部の新しいディレクトリが「秒制限」を「超過」して削除されることがあります。

答え2

Rowanの答えを補完します。ディレクトリパスでポイントを変更できます。

find . -type d -name follower* -mtime +14 -exec rm -rf {} +;

答え3

そしてzsh

(){ n=$#; } follower<->(/)       # count the number of follower<n> dirs

to_remove=(follower<->(/m+13om)) # assumes the dir list is not changed
                                 # since the previous command

(($#to_remove < n)) || to_remove[1]=() # keep the youngest if they're
                                       # all over 2 weeks old



echo rm -rf $to_remove

echo(満足すれば削除)

  • <->一連の10進数(<1-20>無制限の略語)。
  • (){code} args:引数の数が格納される匿名関数です$n
  • (/omm+13): グローバル予選
  • /:ファイル形式のみ選択目次findと同じ-type d
  • m+13:期間が正確に13日を超えるすべてのファイル、つまり14日以上経過したファイル(findsと同じ-mtime +13
  • om:修正時間に基づいてソート(ls -t最新のファイルから)

ディレクトリ変更時間に依存するのは危険です。ファイルが追加、削除、または名前が変更されると(またはディレクトリが編集されると)、touchディレクトリが変更されます。これらのディレクトリには番号が付けられているので、その番号付けを代わりに使用したい場合があるのでomnOn数値はnrder Oinverse(capital O)をnameに)置き換えてください。

変数にパターンを含めるには、設定またはその他の値follower<->に置き換えます。$~patternpattern='follower<->'

答え4

いくつかの回避策:

1. GNUベースfind

#!/bin/bash

# The pattern has to be available to subshells
export patt="$1"

find . -maxdepth 1 -type d -name "${patt}*" -mtime +14 \
  -exec sh -c '[ "$(find . -maxdepth 1 -type d -name "${patt}*" -print0 |
    sort -z -V |
    tail -z -n 1 |
    tr -d "\0")" != "$1" ]' sh {} \; \
  -exec sh -c 'echo rm -r "$1"' sh {} \;

スクリプトは次のように呼び出されます。

./script name_pattern

そのままテスト実行を提供します。実際にディレクトリが削除されるように、echo最後のタスクから削除してください。-exec

それ:

  • 現在のディレクトリで変更されてから14日以上(下記参照)、各値が-mtime;で始まるすべてのディレクトリを見つけます${patt}
  • -exec見つかった(最初の)ディレクトリが昇順にソートされた名前パターンと一致する最後のディレクトリではないことを確認してください。バージョンorder( -V) (たとえば、follower100末尾に置くfollower2)、テスト ( [) が失敗した場合は、find次のループにジャンプして後続の操作を実行しません。
  • 見つかったディレクトリ(2番目のディレクトリ-exec)を削除します。

ここでは、名前に基づいてアルファベット順にディレクトリをソートすることと、変更日に基づいてソートすることとの間に等価であると仮定します。もしあなたの最新ディレクトリは名前に基づいて定義されます。
逆に、あなたの場合最新ディレクトリは最後に変更されたディレクトリです。-exec ...上記のコードの最初のディレクトリを次のディレクトリに置き換える必要があります。

  -exec sh -c '[ "$(find . -maxdepth 1 -type d -name "${patt}*" -printf "%T@\n" |
    sed "s/\..*$//" |
    sort -n |
    tail -n 1)" != "$(stat -c "%Y" "$1")" ]' sh {} \; \

内部的にはfind、名前パターンに一致するすべてのディレクトリを探し、エポック以降の修正時間のリスト(秒単位)を印刷し、小数部を削除して並べ替え、最後の部分を取得し、外部で現在の修正時間と等しくないかどうか確認してくださいfind

このフィルタを使用すると、一致するすべてのディレクトリが14日より古く、変更時間がまったく同じ場合、ディレクトリは削除されません。


メモ:

-maxdepth 1現在、ディレクトリ()の内容に検索を制限する必要があるという厳しい要件はありません。

たとえば、スクリプトの先頭に追加するsort方法を教えておくことができます(参照)。export LC_ALL=C「「LC_ALL = C」は何をしますか?」への答えです。ローカライズ設定によって発生する可能性があるソートの問題について説明します。

を使用すると、-mtime +14修正時間が技術的に14 * 24時間より古い場合でも、14〜15日前に変更されたファイルをスキップします。man find詳細を参照してください。詳細については説明を参照してください-atime n。)

名前にスペース、改行、珍しく印刷できない文字が含まれていても機能します。

互換性:欠点は移植性がないことです。ここで使用されている一部の機能、特に、および、findコマンド、オプションおよびオプション (詳細は忘れてしまったでしょう) は POSIX に指定されていません。-maxdepth-print0-printfstat-Vsort-zsorttail

2. シェル機能ベース

#!/bin/sh

patt="$1"                 # The name pattern
test -z "$patt" && exit   # Caution: pattern must not be empty

days=14     # How old has to be a directory to get deleted, in days?
last=       # The youngest directory

dirs=( "$patt"* )     # Array of files matched by name (note, here we
                      # have everything that matches, not just dirs)
now="$(date +'%s')"   # Now in seconds since Epoch

threshold="$(( "$now" - ( "$days" * 24 * 60 *60 ) ))"
                      # Dirs older than this date (in seconds since
                      # Epoch) are candidates for deletion

# We find the youngest directory in the array
#
for i in "${!dirs[@]}"; do
  if  [ -z "$last" ] ||
    ( [ -d "${dirs[$i]}" ] &&
      [ "$(stat -c '%Y' -- "${dirs[$i]}")" -gt "$(stat -c '%Y' -- "$last")" ] ); then
    last="${dirs[$i]}"
  fi
done

# We delete all the directories in the array that are
# not the youngest one AND are older that the thrashold
#
for i in "${!dirs[@]}"; do
  if  [ -d "${dirs[$i]}" ] &&
      [ "${dirs[$i]}" != "$last" ] &&
      [ "$(stat -c '%Y' -- "${dirs[$i]}")" -lt "$threshold" ]; then
    echo rm -rf -- "${dirs[$i]}"
  fi
done

スクリプトは次のように呼び出す必要があります。

./script name_pattern

echo繰り返しますが、削除するまでテスト実行が提供されますecho rm -rf -- "${dirs[$i]}"

それ:

  • 名前パターンに一致する現在のディレクトリのすべてのファイル名で配列を入力します。
  • 配列の最新のディレクトリを確認してください。
  • 1)14日より古い、2)最新のディレクトリではなく、配列内のすべてのディレクトリを削除します。

メモ:

14日より古いディレクトリを対象としています。(とは異なりますfind)。したがって、両方のソリューションは厳密に同じではありません。
さらに、一致するすべてのディレクトリがしきい値よりも古い場合、変更時間が等しい場合は、ランダムに選択されたディレクトリの1つだけを除いてすべて削除されます。

改行や印刷できない文字を含む珍しい文字を含む名前は問題ありません。

互換性:このソリューションもPOSIXではなく一部の機能、つまりstatformatに依存します%s date。片手ソート、確かに…

関連情報