在Python中移动文件并创建目录,如果有特定的文件类型

4
这可能是一个简单的问题,但我刚接触Python和编程。 我正在编写一个简单的程序,可以将.mp3文件从一个位置复制/移动到另一个位置,并在目标位置上反映源位置的目录结构。 我已经有了一些可行的方案,但是即使源文件夹不包含mp3文件,它也会在目标位置创建新文件夹。 我只想在源包含.mp3文件的情况下创建新目录,否则可能会导致目标中出现大量空文件夹。
以下是我的代码:
import os
import shutil #Used for copying files

##CONFIG
source_dir = "C:\Users\username\Desktop\iTunes\\" #set the root folder that you want to     scan and move files from.  This script will scan recursively.
destPath = "C:\Users\username\Desktop\converted From iTunes" #set the destination root that you want to move files to.  Any non-existing sub directories will be created.
ext = ".mp3" #set the type of file you want to search for.
count = 0 #initialize counter variable to count number of files moved
##

##FIND FILES
for dirName, subdirList, fileList in os.walk(source_dir):

    #set the path for the destination folder(s)
    dest = destPath + dirName.replace(source_dir, '\\') 

    #if the source directory doesn't exist in the destination folder
    #then create a new folder
    if not os.path.isdir(dest):
        os.mkdir(dest)
        print('Directory created at: ' + dest)

    for fname in fileList:
        if fname.endswith(ext) :
            #determine source & new file locations
            oldLoc = dirName + '\\' + fname
            newLoc = dest + '\\' + fname

            if os.path.isfile(newLoc): # check to see if the file already exists.  If it does print out a message saying so.
                print ('file "' + newLoc + fname + '" already exists')

            if not os.path.isfile(newLoc): #if the file doesnt exist then copy it and print out confirmation that is was copied/moved
                try:
                    shutil.move(oldLoc, newLoc)
                    print('File ' + fname + ' copied.')
                    count = count + 1
                except IOError:
                    print('There was an error copying the file:  "' + fname + '"')
                    print 'error'            

print "\n"
print str(count) + " files were moved."
print "\n"

如果文件夹结构类似于:

root->
 band 1->
  album name->
   song.m4a,
   song2.m4a

现在它会在目标目录中创建所有这些文件夹,即使没有要复制的 .mp3 文件.....

非常感谢您的帮助!

3个回答

1
我认为我会为这种情况创建自己的复制包装器:
def fcopy(src,dest):
    """
    Copy file from source to dest.  dest can include an absolute or relative path
    If the path doesn't exist, it gets created
    """
    dest_dir = os.path.dirname(dest)
    try:
        os.makedirs(dest_dir)
    except os.error as e:
        pass #Assume it exists.  This could fail if you don't have permissions, etc...
    shutil.copy(src,dest)

现在您只需遍历树,在任何 .mp3 文件上调用此函数即可。

我需要一些时间来阅读和理解它的工作原理。它看起来很有前途。就像我说的,我对编程非常新手,大多数概念对我都是新的。谢谢。 - dkehler

0
我能想到的最简单的方法是让你现有的代码跳过任何没有 .mp3 文件的文件夹。这可以通过在循环顶部添加以下项和 if 语句来轻松完成。itertools.ifilter()fnmatch.fnmatch() 函数可以一起使用,以简化检查具有正确扩展名的文件的过程。
from itertools import ifilter
from fnmatch import fnmatch

ext = '.mp3'
fnPattern = '*'+ext

for dirName, subdirList, fileList in os.walk(source_dir):
    if not any(ifilter(lambda fname: fnmatch(fname, fnPattern), fileList)):
        print '  skipping "{}"'.format(dirName)
        continue
    ...

您还需要在稍后的代码中将os.mkdir(dest)更改为os.makedirs(dest),以确保任何被早期迭代跳过的子目录在需要复制文件到目标目录的相应子分支时得到创建。

您可以通过创建并保存可能为空的匹配文件迭代器来优化一些内容,该迭代器具有扩展名,然后稍后再次使用它来确定要复制的文件:

from itertools import ifilter
from fnmatch import fnmatch

ext = '.mp3'
fnPattern = '*'+ext

for dirName, subdirList, fileList in os.walk(source_dir):

    # generate list of files in directory with desired extension
    matches = ifilter(lambda fname: fnmatch(fname, fnPattern), fileList)

    # skip subdirectory if it does not contain any files of interest
    if not matches:
        continue
    ...
    ... create destination directory with os.makedirs()
    ...
    #  copy each file to destination directory
    for fname in matches:
      ... copy file

但是你怎么知道它没有任何包含“.mp3”的子目录呢? - mgilson
我尝试修改我的代码,使其在创建目录之前查找mp3文件,但如果有一个文件夹本身不包含mp3文件,但包含一个包含mp3文件的子文件夹,那么这种方法将失败。这看起来会跳过一个文件夹(和它的子文件夹?)如果它不包含mp3文件? - dkehler
@mgilson:os.walk会返回以目录source_dir为根的整个目录树中的每个目录(包括source_dir本身),无论在for循环中是否对每个目录进行处理。这意味着即使通过if/continue跳过了目录本身,该目录下的子目录仍将被处理。 - martineau
@mgilson:处理这个问题的一个简单方法是在将任何文件复制到目标目录之前,对每个目标目录应用os.makedirs() - 这确实需要在OP的代码中进行一些更改。 - martineau
谢谢您关于os.path的建议,我会记在心里。我已经接受了一个答案,但由于我在这里还是新手,无法进行点赞。如果可能的话,一旦我获得足够的积分,我会回来并给予应有的点赞。 :-) - dkehler
显示剩余5条评论

0

使用shutils.copytree函数,可以用更少的代码行实现你想要的功能。


在这里使用 ignore=... 关键字似乎很优雅,但 OP 仍然可能会得到一堆空目录吗? - mgilson
据我理解,shutils.copytree会复制整个文件夹结构。如果一个文件夹中包含.mp3文件,那么我只想复制这个文件夹。 - dkehler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接