2

サーバーがいっぱいになり、ファイルの削除を自動化する必要があります。通常、ファイルは毎日サーバーに追加されますが、一時停止して隔週または毎月に追加されることもあります。彼らは何ヶ月も入ってこなくなり、その後また始まります。それは予測不可能です。

私のスクリプトは、30 日以上経過したファイルを削除する必要がありますが、見つかったファイル パターンの最新の 5 つのファイルを常に保持します。これはトリッキーな部分です。

ファイルに関する唯一の予測可能なこと/パターンは、ファイルが常にどこかに yyyymmddhhmmss タイムスタンプを含み、繰り返されるいくつかのパターンであり、ファイル名の他の部分は常に予測できるとは限りません。ファイルにタイムスタンプがない場合、削除したくありません。

ディレクトリの例には、次のようなものがあります

20121118011335_team1-pathway_Truck_Report_Data_10342532.zip
20121119011335_team1-pathway_Truck_Report_Data_102345234.zip
20121120011335_team1-pathway_Truck_Report_Data_10642224.zip
20121121011335_team1-pathway_Truck_Report_Data_133464.zip
20121122011335_team1-pathway_Truck_Report_Data_126434344.zip
20121123011335_team1-pathway_Truck_Report_Data_12444656.zip
20121124011335_team1-pathway_Truck_Report_Data_1624444.zip
20121125011335_team1-pathway_Truck_Report_Data_3464433.zip
randomefilewithnodate.zip
20121119011335_team2-Paper_Size_Report_336655.zip
20121120011335_team2- Paper_Size_Report_336677.zip
20121121011335_team2-Paper_Size_Report_338877.zip
20121122011335_team2-Paper_Size_Report_226688.zip
20121123011335_team2-Paper_Size_Report_776688.zip
20121124011335_team2-Paper_Size_Report_223355.zip 20121125011335_team2-
Paziper_1_11Report_1_11

このような場合、私のスクリプトは最初のパターンの最も古い 3 つのファイルのみを削除する必要が
あり
ます


および 2 番目のパターンの最も
古い 2 つのファイル

このようにして、5つの最新のファイルを保持し、日付のないファイルに触れません

私の問題は、yyyymmddhhmmss_ の後に何が正確に続くかを知る方法がないことです

これまでのところ、タイム スタンプが存在する場合に一致する正規表現を思いつきましたが、ファイルの残りのパターンを検出して 5 日間のパターンを保持するのに十分なほどスマートなスクリプトを取得する方法が思いつきません。

どんなアイデアでも大歓迎です!以下のスクリプトは完璧ではありません。小さな間違いは修正できます。

主に5つの最新ファイルを保持する部分について本当に助けが必要です

おまけの質問はエポックタイムの部分です。

def myCleansingMethod(self, client)

    # Get rid of things older than 30 days
    # 30 days has this many seconds 30 * 24 * 60 * 60
    numberOfSeconds = 2592000
    # establish what the epoc time of the oldest file I want to keep is
    oldestFileThatIWantToKeep = time.time() - numberOfSeconds
    #establish my working directory
    workingDirectory = "/home/files/%s" % (client)
    try:
        files = os.listdir(workingDirectory)
        except:
        print "Could not find directory"
        return

        files.sort()
        for file in files:
            # define Full File Name (path + file)
            fullFileName = "%s/%s" % (workingDirectory, file)
            # make sure the file contains yyyymmddhhmmss
            match = re.search(r'[0-9]{4}(1[0-2]|0[1-9])(3[01]|[12][0-9]|0[1-9])([01]\d|2[0123])([0-5]\d){2}', file)
            if match:
                #get what was matched in the RegEx
                fileTime = match.group()
                #convert fileTime to Epoc time
                fileTimeToEpoc = (fileTime + NOT SURE HOW TO DO THIS PART YET)

                if fileTimeToEpoc < oldestFileThatIWantToKeep AND (CODE THAT MAKES SURE   THERE ARE AT LEAST 5 FILES OF THE SAME PATTERN PRESENT) :
                print "Delete file: %s\t%s" % (fileTimeToEpoc, fullFileName)
                command = "rm -Rf %s" % fullFileName
                print command
                os.system (command)
                else:
                pass  
            else:
            pass
4

7 に答える 7

3

これは素晴らしい作業です。主に .NET からの関数型パターンを多用しましたitertools。巨大なリストに対してもスケーラブルであり、関連する機能的なアイデアにより、コードが読みやすく保守しやすくなるため、イテレータを使用するのが大好きです。

まず、itertools と datetime から必要なものをインポートします。

from itertools import groupby, chain
from datetime import datetime

サンプル ファイル名をリストとして取得します。

filenames = """20121118011335_team1-pathway_Truck_Report_Data_10342532.zip
20121119011335_team1-pathway_Truck_Report_Data_102345234.zip
20121120011335_team1-pathway_Truck_Report_Data_10642224.zip
20121121011335_team1-pathway_Truck_Report_Data_133464.zip
20121122011335_team1-pathway_Truck_Report_Data_126434344.zip
20121123011335_team1-pathway_Truck_Report_Data_12444656.zip
20121124011335_team1-pathway_Truck_Report_Data_1624444.zip
20121125011335_team1-pathway_Truck_Report_Data_3464433.zip
randomefilewithnodate.zip
20121119011335_team2-Paper_Size_Report_336655.zip
20121120011335_team2-Paper_Size_Report_336677.zip
20121121011335_team2-Paper_Size_Report_338877.zip
20121122011335_team2-Paper_Size_Report_226688.zip
20121123011335_team2-Paper_Size_Report_776688.zip
20121124011335_team2-Paper_Size_Report_223355.zip
20121125011335_team2-Paper_Size_Report_111111.zip""".split("\n")

いくつかのヘルパー関数。名前は自明である必要があります。

def extract_date(s):
    return datetime.strptime(s.split("_")[0], "%Y%m%d%H%M%S")

def starts_with_date(s):
    try:
        extract_date(s)
        return True
    except Exception:
        return False

すべてのケースをカバーしていない場合に調整する必要がある次の方法-サンプルデータの場合、それはカバーします。

def get_name_root(s):
    return "".join(s.split(".")[0].split("_")[1:-1])

def find_files_to_delete_for_group(group):
    sorted_group = sorted(group, key=extract_date)
    return sorted_group[:-5]        

これで、ルーチン全体を繰り返し実行できます。最初に、ファイル名のリストをフィルター処理します。データで始まらないもの (ご使用の形式) はすべて除外されます。次に、残りは「名前のルート」でグループ化されます (これ以上の名前は思いつきません)。

fn_groups = groupby(
                filter(
                    starts_with_date,
                    filenames),
                get_name_root
            )

ここで、グループごとにフィルタリング方法 (上記を参照) を適用して、最新の 5 つの日付を含まないすべてのファイル名を見つけます。各グループで見つかったものがchained です。つまり、複数のリストから 1 つの反復子が作成されます。

fns_to_delete = chain(*[find_files_to_delete_for_group(g) for k, g in fn_groups])

最後に、結果を簡単に確認するために、イテレータをリストに変換して出力します。

print list(fns_to_delete)

このスクリプトの出力は次のとおりです。

['20121118011335_team1-pathway_Truck_Report_Data_10342532.zip', '20121119011335_team1-pathway_Truck_Report_Data_102345234.zip', '20121120011335_team1-pathway_Truck_Report_Data_10642224.zip', '20121119011335_team2-Paper_Size_Report_336655.zip', '20121120011335_team2-Paper_Size_Report_336677.zip']

不明な点があれば、質問してください。

そして、これが単純なc&p-ingのスクリプト全体です。

from itertools import groupby, chain
from datetime import datetime

filenames = """20121118011335_team1-pathway_Truck_Report_Data_10342532.zip
20121119011335_team1-pathway_Truck_Report_Data_102345234.zip
20121120011335_team1-pathway_Truck_Report_Data_10642224.zip
20121121011335_team1-pathway_Truck_Report_Data_133464.zip
20121122011335_team1-pathway_Truck_Report_Data_126434344.zip
20121123011335_team1-pathway_Truck_Report_Data_12444656.zip
20121124011335_team1-pathway_Truck_Report_Data_1624444.zip
20121125011335_team1-pathway_Truck_Report_Data_3464433.zip
randomefilewithnodate.zip
20121119011335_team2-Paper_Size_Report_336655.zip
20121120011335_team2-Paper_Size_Report_336677.zip
20121121011335_team2-Paper_Size_Report_338877.zip
20121122011335_team2-Paper_Size_Report_226688.zip
20121123011335_team2-Paper_Size_Report_776688.zip
20121124011335_team2-Paper_Size_Report_223355.zip
20121125011335_team2-Paper_Size_Report_111111.zip""".split("\n")

def extract_date(s):
    return datetime.strptime(s.split("_")[0], "%Y%m%d%H%M%S")

def starts_with_date(s):
    try:
        extract_date(s)
        return True
    except Exception:
        return False

def get_name_root(s):
    return "".join(s.split(".")[0].split("_")[1:-1])

def find_files_to_delete_for_group(group):
    sorted_group = sorted(group, key=extract_date)
    return sorted_group[:-5]        

fn_groups = groupby(
                filter(
                    starts_with_date,
                    filenames),
                get_name_root
            )

fns_to_delete = chain(*[find_files_to_delete_for_group(g) for k, g in fn_groups])

print list(fns_to_delete)
于 2013-02-06T08:38:48.613 に答える
1

ファイルに関する唯一の予測可能なもの/パターンは、ファイルには常にyyyymmddhhmmssタイムスタンプがどこかに含まれ、パターンが繰り返されることです。

ファイル名のどこでも許可yyyymmddhhmmssし、繰り返しパターンを自動的に見つけるには、最初yyyymmddhhmmssにファイル名から削除してから、繰り返しパターンとして少なくとも2回繰り返される最長のプレフィックスを使用します。

import os
from itertools import groupby
from os.path import commonprefix

def files_to_delete(topdir):
    for rootdir, dirs, files in os.walk(topdir):
        # find files with yyyymmddhhmmss
        files_with_date = []
        for filename in files:
            for m in re.finditer(r"(?:^|\D)(\d{14})(?:\D|$)", filename):
                date = parse_date(m.group(1))
                if date is not None: # found date in the filename
                   # strip date
                   no_date = filename[:m.start(1)] + filename[m.end(1):]
                   # add to candidates for removal
                   files_with_date.append((no_date, date, filename))
                   break

        # find repeating pattern
        files_with_date.sort() # sort by filename with a removed date
        # given ["team1-a", "team2-b", "team2-c"]
        # yield [["team1-a"], ["team2-b", "team2-c"]] where 
        #    roots are "team1" and "team2"
        # reject [["team1-a", "team2-b", "team2-c"]] grouping (root "team")
        #     because the longer root "team2" occurs more than once
        roots = [commonprefix(a[0],b[0]) for a,b in pairwise(files_with_date)]
        roots.sort(key=len, reverse=True) # longest roots first
        def longest_root(item):
            no_date = item[0]
            return next(r for r in roots if no_date.startswith(r)) or no_date
        for common_root, group in groupby(files_with_date, key=longest_root):
            # strip 5 newest items (sort by date)
            for _, d, filename in sorted(group, key=lambda item: item[1])[:-5]:
                if d < month_ago: # older than 30 days
                   yield os.path.join(rootdir, filename)

注:繰り返しパターンとして使用し['team1-a', 'team2-b', 'team3-c', ...]てグループ化されます。つまり、ファイルリストで「繰り返しパターン」が繰り返されない場合、上記のアルゴリズムは失敗します。[['team1-a', 'team2-b', 'team3-c', ...]]'team'

ユーティリティ:

from datetime import datetime, timedelta
from itertools import izip, tee

month_ago = datetime.utcnow() - timedelta(days=30)

def parse_date(yyyymmddhhmmss):
    try: return datetime.strptime(yyyymmddhhmmss, "%Y%m%d%H%M%S")
    except ValueError:
         return None

def pairwise(iterable): # itertools recipe
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)       

os.remove(path)ファイルを削除するには、の代わりに呼び出すことができますos.system()

その後、ファイルの命名スキームをより決定論的なものに変更できる場合、たとえば、[]ファイル名のパターンの周りを使用すると、rootを次のように抽出できます。

root = re.match(r'[^[]*\[([^]]+)\]', filename).group(1)
于 2013-02-06T11:33:15.927 に答える
1

あなたがしなければならないことの難しい部分は、コーディングの問題ではなく、定義の問題なので、より良いコードを書くだけでは解決できません :-)

が とと20121125011335_team1-pathway_Truck_Report_Data_3464433.zip同じグループに属しているのはなぜですか? 重要な共通部分がであり、 ではないことを (人間として) どのように認識しましたか?20121118011335_team1-pathway_Truck_Report_Data_10342532.zip20121119011335_team1-pathway_Truck_Report_Data_102345234.zip_team1-pathway_Truck_Report_Data__team1-pathway_Truck_Report_Data_1

その質問に答えてください (答えには「アンダースコア」や「数字」という言葉が含まれていると思います)。

yyyymmddhhmmss_something_consistent_random_random または yyyymmddhhmmss_something_consistent_something_consistent_random_random.xyz のさまざまな反復になることだけを知っています

それがすべての可能なバリエーションである場合、アンダースコアで囲まれた共通の初期シーケンスを探す必要があると言えます。ランダムなものは常に最後にあるため、これは機能します。そのため、ファイル拡張子を重要なものとして含めたい場合は、それを特別に処理する必要があります (たとえば、比較している文字列の前に移動します)。共通の「単​​語」が 3 つあるが 4 つでない複数のファイルが見つかった場合、4 番目のブロックは「ランダム」であり、3 つのブロックは「一貫性がある」と想定します。次に、その種類のすべてのファイルを日付順に並べ替え、リストから最新の 5 つを削除し、残りの 30 日以上経過したファイルをすべて削除します。

これらの一般的な初期シーケンスを見つける「明らかな」方法は、ファイル名を日付以外のコンポーネントの辞書編集順にソートすることです。次に、共通の初期シーケンスを持つファイルが隣接しているため、リストを反復処理して、各ファイルを共通のプレフィックスを持つファイルの現在の最長ランと比較できます。

コーディング中に、次のケースが考えられる場合は、正しく処理するようにしてください。

<some_date>_truck1_548372.zip
<some_date>_truck1_847284.zip
<some_date>_truck1_data_4948739.zip
<some_date>_truck1_data_9487203.zip

つまり、この場合は 1 つのグループ ("truck1") を扱っているのか、2 つのグループ ("truck1" と "truck1_data") を扱っているのかを確認してください。truck1_data5 つのファイルを保持するという要件から任意のファイルを除外したい場合があるため、これは重要truck1です。


別のアプローチ:

  • 30 日以上経過したすべてのファイル (例: <some_date>_truck1_57349.zip) を検索し、古いものから新しいものへと並べ替えます。
  • 各ファイルに対して、次のように「許可」を求めて削除します。
    • ファイル名の先頭から日付を削除します
    • 独自の日付を無視して、このファイルと共通の最初のアンダースコアで囲まれた部分文字列を持つすべてのファイル (30 日以上経過したファイルだけでなく) を検索します (したがって、ここでtruck1ファイルとファイルを検索しtruck1_dataます) 。
    • それらのファイルを見つけたら、そのうちの少なくとも 2 つが共有する最長の部分文字列を見つけます ( truck1_data)
    • ターゲット ファイルがその部分文字列を共有していない場合は、共通の部分文字列を持つすべてのファイルをセットから削除し、前の手順を繰り返します (これで、truck1ファイルだけになります) 。
    • ターゲット ファイルが部分文字列を共有したら、それらをカウントします。5 つ以上ある場合は、対象のファイルを削除します。

述べたように、これは不必要に遅いですが、要点を簡単に示していると思います。ファイルのグループを特定したので、最後のステップで残りの 5 つのファイルを除くすべてのファイルを実際に削除し、残りの 5 つを今後の検討対象から外すことができます。同様に、ターゲット ファイルと共有する部分文字列よりも長い共通部分文字列を持つすべてのファイルを削除すると、グループが識別され、将来の識別のために単に海に戻すのではなく、1 つのグループとして処理できます。

于 2013-02-06T09:36:12.543 に答える
0

問題を分離するだけで、1 つの方法でパターンを見つけ、もう 1 つの方法で日付を見つけて何かを削除することができます。

import os
import subprocess

def find_patterns():
    filenames = os.walk("/path/to/my/data/dir").next()[2]
    parsed_patterns = map(lambda file: "_".join(file.split("_")[1:-1]),filenames)
    unique_patterns = list(set(parsed_patterns))
    return unique_patterns

def delete_dates_after_past_five_days(unique_patterns):
    filenames = os.walk("/path/to/my/data/dir").next()[2]
    for pattern in unique_patterns:
        matched_files = filter(lambda file: pattern in file, filenames)
    sorted_files = sorted(matched_files) #Will sort by date
    if len(sorted_files) > 5:
        for file in sorted_files[:-5]:
            subprocess.call(["rm", os.path.join("/path/to/my/data/dir", file)])

unique_patterns = find_patterns()
delete_dates_from_past_five_days(unique_patterns) 
于 2013-02-06T08:49:42.247 に答える
0

以前のランダム部分が.zip常に数字とアンダースコアで構成されている場合、この正規表現を使用できます

(\d{14})_(.*?)[_\d]+\.zip

一致する最初のグループは、 を使用して確認および解析できる可能性のある日付ですdatetime.datetime.strptime。2 番目のグループは、グループ化に使用する定数部分です。

于 2013-02-06T08:41:32.740 に答える
0

私のおすすめ:

  • 名前に 14 桁が連続するファイルが見つかるまで、ファイルを検索します。
  • これらの数字がタイムスタンプであるかどうかを確認してください。
  • 名前の残りが同じである他のすべてのファイルを検索します。
  • 日付の降順に並べ替えます。
  • 順序付きファイル リストの 6 番目のファイルから 5 日以上経過したすべてのファイルを削除します。
于 2013-02-06T08:27:02.410 に答える
0

import timeこの関数は、日付スタンプを含むファイル名をエポック時間に変換します。正規表現は使用しません。

def timestamp2epoch(str):
   x=[]
   for v in (str[0:4],str[4:6],str[6:8],str[8:10],str[10:12],str[12:14],0,0,0):
     x.append(int(v))
   return time.mktime(x)
于 2013-02-06T08:45:36.743 に答える