繁体   English   中英

在更改目录名后,os.walk()重命名文件夹和文件时会丢失一些文件

[英]Renaming folders and files while os.walk()ing them missed some files after change of the directory name

我有这样的文件夹结构:

Template
  - Template1
  - Template2
TemplateTest
  - TemplateTest1
Config
  - TemplateConfig

我想用'MyApp'替换每个文件名和每个文件夹名称的'Template'。

这是我的代码:

for root, dirs, files in os.walk(path):
    for name in files:
        if name.startswith("Template"):
            replace = name.replace("Template",'MyApp')
            os.rename(os.path.join(root,name),os.path.join(root,name.replace(old,new)))
    for name in dirs:
        if name.startswith("Template"):
            replace = name.replace("Template",'MyApp')
            os.rename(os.path.join(root,name),os.path.join(root,replace))

奇怪的是,这只会替换文件夹名称和父文件夹名称不需要更改的文件名。 像这样:

MyApp
  - Template1
  - Template2
MyAppTest
  - TemplateTest1
Config
  - MyAppConfig

但如果我执行此代码两次,它将替换文件。 我想知道为什么以及如何更改代码以便它取代我需要的一切?

(请注意, os.walk的调用签名是:

os.walk = walk(top, topdown=True, onerror=None, followlinks=False)

所以你传递的是TrueNoneFalse 。)

问题与os.walk目录和文件的顺序以及它进入的目录和文件有关。

特别是,它首先读取path的目录。 这产生以下结果:

['Template', 'TemplateTest', 'Config']

所有这些都是目录,所以它下次走的子目录列表是相同的,没有文件。 这将作为第一次迭代中的三个值返回:

path
['Template', 'TemplateTest', 'Config']
[]

然后,您可以使用自己的代码,在Template调用os.rename ,使其现在命名为MyApp ,然后在TemplateTest ,这样该目录现在名为MyAppTest

接下来, os.walk代码尝试读取子目录Template 这种失败,所以什么也不会发生( onerrorNone )。

接下来, os.walk代码尝试读取子目录TemplateTest 这失败了,所以没有任何反应。

最后, os.walk代码尝试读取子目录Config 这成功了,一切顺利。

有两种不同的解决方案:您可以将topdown设置为False ,或者您可以更新名为dirs的列表,以便os.walk知道dirs名称。 (编辑:我不确定topdown=False会修复它;这需要测试。)

如有疑问 - print出来:

创建数据结构:

import os


for d in ["./Template","./TemplateTest","./Config"]:
    os.mkdir(d)

for f in ["./Template/Template1.txt","./Template/Template2.txt",
          "./TemplateTest/TemplateTest1.txt", "./Config/TemplateConfig.txt"]:
    with open(f,"w") as f:
        f.write(" ")

测试os.walk

for root, dirs, files in os.walk("./"): # no topdown means == True
    for name in files:
        if name.startswith("Template"):
            replace = name.replace("Template",'MyApp')
            print("renaming: ", os.path.join(root,name), " to ", os.path.join(root,replace))
            # os.rename(os.path.join(root,name),os.path.join(root,replace))
    for name in dirs:
        if name.startswith("Template"):
            replace = name.replace("Template",'MyApp')
            print("renaming: ", os.path.join(root,name), " to ", os.path.join(root,replace))
            # os.rename(os.path.join(root,name),os.path.join(root,replace))    

输出如果您注释掉for ... loops并且只print(root,dirs,files)

./             ['Config', 'Template', 'TemplateTest'] ['main.py']
./Config       []                                     ['TemplateConfig.txt']
./Template     []                                     ['Template1.txt', 'Template2.txt']
./TemplateTest []                                     ['TemplateTest1.txt']

如果再次注释for循环并用print替换重命名,则会得到:

renaming:  ./Template  to  ./MyApp            # aha - works
renaming:  ./TemplateTest  to  ./MyAppTest    # aha - works 
renaming:  ./Config/TemplateConfig.txt  to  ./Config/MyAppConfig.txt   # works
renaming:  ./Template/Template1.txt  to  ./Template/MyApp1.txt       # folder not updated
renaming:  ./Template/Template2.txt  to  ./Template/MyApp2.txt       # folder also not updated
renaming:  ./TemplateTest/TemplateTest1.txt  to  ./TemplateTest/MyAppTest1.txt  # also not updated

如果您看一下文档,它可能会说在迭代生成的os.walk()结果时的更改不会反映在生成的数据中。

你基本上“在迭代时改变一个可互换的”; o)

从链接的doku:

topdownTrue ,调用者可以就地修改dirnames列表(可能使用del或slice赋值),而walk()只会递归到名称保留在dirnames中的子目录中; 这可用于修剪搜索,强制执行特定的访问顺序,甚至可以告诉walk()有关调用者在再次恢复walk()之前创建或重命名的目录。 topdownFalse时修改dirnames对walk的行为没有影响,因为在自下而上模式中,dirnames中的目录是在生成dirpath本身之前生成的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM