Windows 10 文件历史记录/备份中限制世代






4.20/5 (2投票s)
防止新的 Windows 备份机制臃肿
引言
新的 Windows 文件历史记录实现了其目标,成为一种易于设置的存档和备份机制,无需过多干预即可实现相当复杂的多世代文件历史记录。 唯一的问题是,使用默认的“备份我的文件”设置(一小时),对于不断更新的文件,项目数量会迅速增加。 这个小工具允许您定期修剪并仅保留文件历史记录/备份集中的最后 n 个文件。
代码
本文旨在提供一个有用的工具,而不是展示任何花哨的编码技巧。 这是我第一次提交代码。 我必须说,想到其他人会查看你的代码会集中注意力。 我没有采用通常的快速而粗糙的方法,而是努力做到“正确”,进行一些错误检查,甚至偶尔添加一些注释。
一个功能需要一些解释。 找到要清除的文件的一种直观方法是对文件进行排序,然后扫描查找原始文件名(即括号“(存档日期)”之前的部分)的重复项。 问题是后缀(例如“.txt”)没有被考虑在内,具有相同基本名称但不同后缀的文件都会被组合在一起,以确定有多少个文件存在。 我通过创建一个排序数组来解决这个问题,该数组由
- 后缀
- 一个占位符 '*'(使用是因为它永远不能是文件名或目录名的一部分)
- 完整路径
然后,该数组按降序排序,以便将最新文件放在最前面。 “比较名称”是最后一个 '(' 之前的文本,而原始路径/文件名是 '*' 之后的文本。 在后缀包含括号的相当晦涩的情况下,此技术将失败 - 总是会有局限性。
我想添加功能,以便除了或加上世代数量外,文件历史记录由例如,一个至少一个月前的文件,一个至少一周前的文件和一个至少一天前的文件组成。 任何有空闲时间的聪明年轻人都可以尝试一下。
我非常重视评论,以及对这位老家伙做事方式的建议替代方案。
// Eliminate old files from Windows 10 File History
//The command line parameters are:
//1 Root directory of target
//2 Number of generations to retain
// Note that the Read-only attribute set by Windows is ignored & overridden to allow deletion
// The program is intended to run in batch mode and so errors are listed without intervention
using System;
using System.IO;
using System.Text;
using System.Collections;
public class RecursiveFileProcessor
{
public static void Main(string[] args)
{
int MaxRetain;
if (args.Length != 2 || !int.TryParse(args[1], out MaxRetain))
{
Console.WriteLine("Usage - " + System.AppDomain.CurrentDomain.FriendlyName + " Path NumberOfGenerations ");
Console.WriteLine("For example " + System.AppDomain.CurrentDomain.FriendlyName + " C:\\FileHistory 2");
return;
}
if (Directory.Exists(args[0]))
{
// This path is a directory
ProcessDirectory(args[0], MaxRetain);
}
else
{
Console.WriteLine("{0} is not available or is not a valid directory.", args[0]);
}
}
// Process all files in the directory passed in, recurse on any directories
// that are found, and process the files they contain.
public static void ProcessDirectory(string targetDirectory, int MaxRetain)
{
string[] fileEntries = Directory.GetFiles(targetDirectory);
string[] SortEntries = new string[fileEntries.Length]; // copy entries but prepend with
// suffix (e.g. .txt) and a placemarker '*' This character was chosen because it cannot be part of filename
// all this is to prevent files with same name but different suffixes from being treated
// as if they form part of the same history set
if (SortEntries.Length > 0)
{
int x = 0;
foreach (string fileName in fileEntries)
{
int LastClose = fileName.LastIndexOf(')');
int LenSuffix = fileName.Length - LastClose - 1;
SortEntries[x] = "";
if (LastClose > 0 && LenSuffix > 0)
SortEntries[x] = fileName.Substring(fileName.Length - LenSuffix);
SortEntries[x] = SortEntries[x] + '*';
SortEntries[x] = SortEntries[x] + fileName;
x++;
}
Array.Sort(SortEntries);
Array.Reverse(SortEntries); // newest files first within set
string PrevName = "";
int CountDup = 0;
foreach (string SortFile in SortEntries)
{
if (SortFile == null) continue;
string OriginalName = SortFile.Substring(SortFile.IndexOf('*') + 1);
int OpenPos = SortFile.LastIndexOf('(');
string CompareName = SortFile;
if (OpenPos > 0)
{
CompareName = SortFile.Substring(0, SortFile.LastIndexOf('('));
}
if (CompareName == PrevName)
{
CountDup++;
}
else
{
CountDup = 0;
PrevName = CompareName;
}
if (CountDup >= MaxRetain)
{
try
{
FileAttributes attributes = File.GetAttributes(OriginalName);
File.SetAttributes(OriginalName, attributes & ~FileAttributes.ReadOnly); // remove Read Only
File.Delete(OriginalName);
Console.WriteLine("{0} deleted", OriginalName);
}
catch(Exception e) // this will handle sharing violations and permission issues
{
Console.WriteLine( "Unable to delete {0} because {1}", OriginalName, e);
continue;
}
}
}
}
// Recurse into subdirectories of this directory.
string[] subdirectoryEntries = Directory.GetDirectories(targetDirectory);
foreach (string subdirectory in subdirectoryEntries)
ProcessDirectory(subdirectory, MaxRetain);
}
}