当前位置: 首页 > 科技观察

Windows下使用Python删除长路径文件_0

时间:2023-03-13 05:12:33 科技观察

0x01文章背景最近,笔者所在公司某业务系统存储接近极限,服务器很快无法运行,原因是业务系统A包含多个子系统A1、A2、A3……An,由于设计原因,这些子系统的中间存储文件都存放在同一个父目录下。唯一不同的是,不同子系统生成的文件名和文件夹名都是以这个系统名开头的。比如A1子系统生成的文件名都是A1xxxxxx,A2子系统生成的文件名都是A2xxxxx。现在我们要删除部分子系统的历史文件,以释放服务器空间。数十TB的数据存储在一起。手动删除肯定不会显示。它只能借助程序自动化来实现。你用什么?自然而然地,蟒蛇浮现在脑海中。其实对于简单删除文件的需求,我觉得不值得长篇大论,但是我遇到了一些特别有趣的问题和一些有趣的解决方案,所以想和大家分享一下,比如超长的删除windows系统下的文件,比如从ReadtheofficialEnglishdocumentstofindsolutions等,进入正题。0x02使用python删除文件使用python删除文件的方法有很多种,最直接方便的方法是调用内置函数:os.remove()删除文件os.rmdir()删除空文件夹shutil.rmtree()删除一个文件夹和文件夹下的所有内容(包括子目录和文件),也就是解决这个问题的方法,核心就是处理上面三个函数。回到我们遇到的问题,业务系统A包含多个子系统A1、A2、A3……An,这些子系统的中间存储文件由于设计原因,都存储在同一个父目录下,唯一的区别是,不同子系统生成的文件名和文件夹名均以该子系统名开头。比如A1子系统生成的文件命名方式是A1xxxxxx,A2子系统生成的文件名都是A2xxxxx。现在的目的是删除指定子系统产生的文件,保留其他子系统的文件。反汇编需求实际上解决了以下四个问题:1、如何删除一个文件?2、如何识别某个文件或文件夹是由某个子系统生成的?3、如何判断一个路径是文件还是目录?4、如何定位指定子系统生成的所有文件和文件夹?对于问题1,本节开头解释,使用python内置函数删除:os.remove("path")#删除指定文件os.rmdir("path")#删除一个空文件夹shutil.rmtree("path")#删除一个文件夹及其所有内容(包括子目录和文件)对于问题2,具体子系统生成的文件和文件夹的命名方式是固定的,比如A1子系统生成的文件名都是A1xxxxx,所以可以通过关键词匹配来识别。一种可能的方式是:ifkeywordsinfilepath:#如果文件名中包含关键字os.remove(filepath)#删除文件else:pass对于问题3,因为删除目录的方式与删除文件的方式不一致,需要tobedelete在判断路径是目录还是文件之前,根据其类型选择合适的删除方式。这个可以通过python中的**os.path.isdir()**等函数来判断,主要有以下几个函数:os.path.isdir("path")#returntrue是一个目录,false是一个文件os.path.isfile("path")#returntrue是一个文件,false是一个目录对于问题4,如何定位所有要删除的文件,这个问题其实是遍历指定目录下的文件的问题,也就是如何遍历指定目录下的所有文件夹和文件。对于这个问题,一般有两种解法,一种是深度优先遍历法,一种是广度优先遍历法。在这个例子中两种方法的效率是一样的,因为我们最终会遍历所有的文件。另外,幸好python太强大了,它的内置函数已经帮我们实现了一个广度优先的目录遍历方法,os.walk("path")方法,就是遍历path目录下的所有文件和文件夹,一个典型的用法如下:importospath="C:\\A\\"forroot,dirs,filesinos.walk(path):print(root)print(dirs)print(files)以上example其中,root代表当前遍历的路径,dirs代表当前路径下的所有子目录,files代表当前路径下的所有子文件。这样就可以遍历所有指定的目录了。问题分解,结合下面问题完成代码实现。最终的代码实现是:importosimportshutilpath="C:\\A\\"keyword="A1"forroot,dirs,filesinos.walk(path):fordirindirs:ifkeywordindir:rmpath=os.path.join(root,dir)print("Deletefolder:%s"%rmpath)shutil.rmtree(rmpath)forfileinfiles:ifkeywordinfile:rmpath=os.path.join(root,file)print("Deletefile:%s"%rmpath)os.remove(rmpath)通过广度优先方法遍历规范(os.walk())directory,一一判断目录下的所有子目录和文件是否满足关键字条件,满足则删除。运行效果是:貌似需求基本解决的很好,但是在实际测试中发现有些深层目录没有删除,删除目录的时候报错。错误描述如下:Unexpectederror:(,WindowsError(3,'Thesystemcannotfindthepathspecified'),)大致意思是python找不到这个路径,但为什么?为此,我继续进行了一些资料查询,后来大致定位到了文件路径过长的原因,因为windows系统的用户态默认路径长度不能超过256字节。但是官方说256字节是最长的,但是为什么超过256字节就可以创建呢,那么既然可以创建就一定要删除,但是需要一些方法。经过一番研究,我找到了几种方法。下面介绍一种最实用的方法,其他的,比如用压缩软件压缩删除(百度知道的结果)适合手工,不适合编程。此方法将在下一节中继续。0x03Windows文件系统对长路径文件的定义解决Windows下长文件删除问题,最权威的资料是Windows的官方说明。看了微软对文件名长度的定义和描述,找到了解决方法,微软原文如下:关键意思如下:1.WindowsAPI提供的文件路径理论上最大为32767字节,其实不是在正常情况下,超过256个字符供用户使用。据说是为了让用户操作更简单。方便的。在这里不得不吐槽一下,操作确实方便,但同时也可能带来不便。明明定义了32767个字节的长度,却只用了256个,未免有点草率。2、如果用户想突破这个长度限制,可以通过特殊的方式告诉windows系统你要使用一个超长的文件。这种特殊的方式是在绝对路径前加上**“\?”**字符串。3、本文档最后还有Windows10之后如何通过注册表访问文件名长度限制的说明,这里就不截图了,因为不通用,Win7呢?感兴趣的同学可以查看原文链接阅读:https://docs.microsoft.com/en-US/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd好了,看到这个,解决方法呼之欲出,其实也不太简单,加个“\?”就可以了。beforetheabsolutepath:#获取目标路径的绝对路径,并在路径前加上\\?\,#删除windows文件长度限制path='\\\\?\\'+os.path.abspath(path)0x04改造python程序,删除长路径文件根据上一节,进一步改造python程序,加上windows去掉长文件名限制,最终完美的删除工具就形成了:importosimportshutilpath="C:\\A\\"keyword="A1"#获取目标路径的绝对路径,并在路径前加上\\?\,#解除Windows文件长度限制path='\\\\?\\'+os.path.abspath(path)forroot,dirs,filesinos.walk(path):fordirindirs:ifkeywordindir:rmpath=os.path.join(root,dir)print("删除文件夹:%s"%rmpath)shutil.rmtree(rmpath)forfileinfiles:ifkeywordinfile:rmpath=os.path.join(root,file)print("Deletefile:%s"%rmpath)os.remove(rmpath)虽然代码很短,只加了一行,但是这一行已经完成一个超级核心的任务,真的是一条灵魂线,最后,如果工具在生产环境中发挥了它的出色作用,服务器继续运行起来像飞0x04想来啰嗦就不多说了。几点思考:1、遇到问题分解问题,一步步分解成小问题。2、善于阅读官方技术文档。有时候解决一个问题的核心可能很简单,代码可能只有一两行,却藏在一个角落里,不仔细看可能发现不了。3.Python是个好东西。如果你有把问题转化为用python解决的习惯,习惯就自然而然,python可能会在你的工作中发挥很大的作用。0x05参考1.https://docs.microsoft.com/en-US/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd2.https://stackoverflow.com/questions/6996603/如何-to-delete-a-file-or-folder-in