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

时间:2018-11-02 18:16:22

标签: python os.walk

我有这样的文件夹结构:

> glimpse(processed_mtcars)
Observations: 32
Variables: 17
$ mpg              <dbl> 21.0, 21.0, 22.8, 21.4, 18.7, 18.1, 14.3, 24.4, 22.8, 19.2, 17.8, 16.4, 17.3, 15.2, 10.4, 10.4, ...
$ cyl              <dbl> 6, 6, 4, 6, 8, 6, 8, 4, 4, 6, 6, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 8, 8, 8, 8, 4, 4, 4, 8, 6, 8, 4
$ disp             <chr> "160", "NULL", "108", "NULL", "360", "225", "360", "NULL", "140.8", "167.6", "167.6", "275.8", "...
$ hp               <dbl> 110, 110, 93, 110, 175, 105, 245, 62, 95, 123, 123, 180, 180, 180, 205, 215, 230, 66, 52, 65, 97...
$ drat             <dbl> 3.90, 3.90, 3.85, 3.08, 3.15, 2.76, 3.21, 3.69, 3.92, 3.92, 3.92, 3.07, 3.07, 3.07, 2.93, 3.00, ...
$ wt               <chr> "2.62", "2.875", "2.32", "3.215", "3.44", "3.46", "3.57", "3.19", "3.15", "NULL", "3.44", "NULL"...
$ qsec             <chr> "16.46", "NULL", "NULL", "NULL", "17.02", "20.22", "15.84", "20", "22.9", "18.3", "18.9", "17.4"...
$ vs               <dbl> 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1
$ am               <dbl> 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1
$ gear             <dbl> 4, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 4, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 4
$ carb             <dbl> 4, 4, 1, 1, 2, 1, 4, 2, 2, 4, 4, 3, 3, 3, 4, 4, 4, 1, 2, 1, 1, 2, 2, 4, 2, 1, 2, 2, 4, 6, 8, 2
$ disp_str_replace <chr> "160", "0", "108", "0", "360", "225", "360", "0", "140.8", "167.6", "167.6", "275.8", "275.8", "...
$ wt_str_replace   <chr> "2.62", "2.875", "2.32", "3.215", "3.44", "3.46", "3.57", "3.19", "3.15", "0", "3.44", "0", "3.7...
$ qsec_str_replace <chr> "16.46", "0", "0", "0", "17.02", "20.22", "15.84", "20", "22.9", "18.3", "18.9", "17.4", "17.6",...
$ disp_as.numeric  <dbl> 160.0, NA, 108.0, NA, 360.0, 225.0, 360.0, NA, 140.8, 167.6, 167.6, 275.8, 275.8, 275.8, 472.0, ...
$ wt_as.numeric    <dbl> 2.620, 2.875, 2.320, 3.215, 3.440, 3.460, 3.570, 3.190, 3.150, NA, 3.440, NA, 3.730, 3.780, 5.25...
$ qsec_as.numeric  <dbl> 16.46, NA, NA, NA, 17.02, 20.22, 15.84, 20.00, 22.90, 18.30, 18.90, 17.40, 17.60, 18.00, 17.98, ...

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

这是我的代码:

Template
  - Template1
  - Template2
TemplateTest
  - TemplateTest1
Config
  - TemplateConfig

奇怪的是,这仅替换了文件夹名称,而父文件夹名称不需要更改的文件名。像这样:

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))

但是如果我执行两次此代码,它将替换文件。 我想知道为什么以及如何更改代码,以替换我需要的所有内容?

2 个答案:

答案 0 :(得分:2)

(请注意,os.walk的呼叫签名为:

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

因此您要传递TrueNoneFalse。)

问题与os.walk遍历目录和文件的顺序以及遍历目录和文件的顺序有关。

尤其是,它从读取path的目录开始。这将产生以下内容:

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

所有这些都是目录,因此下次将要遍历的子目录列表相同,并且没有文件。它将在第一次迭代中作为三个值返回:

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

然后执行自己的代码,在其中,您在os.rename上调用Template,现在将其命名为MyApp,并在TemplateTest上将目录命名为现在命名为MyAppTest

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

接下来,os.walk代码尝试读取子目录TemplateTest。这失败了,所以什么也没发生。

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

有两种不同的解决方案:您可以将topdown设置为False,也可以更新名为dirs的列表,以便os.walk知道 new < / em>目录名称。 (编辑:我不确定topdown=False是否会解决它;这需要测试。)

答案 1 :(得分:1)

如有疑问-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时,调用者可以就地修改目录名列表(可能使用del或slice分配),并且walk()仅递归到名称保留的子目录中在目录名中这可以用来简化搜索,施加特定的访问顺序,甚至可以在重新恢复walk()之前通知walk()呼叫者创建或重命名的目录。   当topdownFalse时修改目录名对走动的行为没有影响,因为在自下而上的模式下,目录名中的目录是在生成目录路径本身之前生成的。