【问题标题】:create destination path for shutil.copy files为 shutil.copy 文件创建目标路径
【发布时间】:2011-02-17 03:18:52
【问题描述】:

如果./a/b/c中不存在b/c/之类的路径,shutil.copy("./blah.txt", "./a/b/c/blah.txt")会报错目的地不存在。创建目标路径并将文件复制到此路径的最佳方法是什么?

【问题讨论】:

    标签: python


    【解决方案1】:

    从给定的答案和 cmets 中总结信息:

    对于 python 3.2+

    os.makedirscopy 之前与exist_ok=True

    os.makedirs(os.path.dirname(dest_fpath), exist_ok=True)
    shutil.copy(src_fpath, dest_fpath)
    

    对于python

    os.makedirs 捕捉到IOError 并再次尝试复制:

    try:
        shutil.copy(src_fpath, dest_fpath)
    except IOError as io_err:
        os.makedirs(os.path.dirname(dest_fpath))
        shutil.copy(src_fpath, dest_fpath)
    

    虽然您可以更明确地检查errno 和/或检查路径exists 是否在makedirs 之前,恕我直言,这些sn-ps 在简单性和功能性之间取得了很好的平衡。

    【讨论】:

      【解决方案2】:

      使用os.makedirs 创建目录树。

      【讨论】:

      • 请注意,exists_ok 选项仅存在于 Python 3.2+ 中
      • 这是exist_ok,而不是exists_ok
      【解决方案3】:

      我使用类似的东西来检查目录是否存在,然后再使用它。

      if not os.path.exists('a/b/c/'):
          os.mkdir('a/b/c')
      

      【讨论】:

      • 据我所知,这在 Python 2.7 中不起作用:OSError: [Errno 2] No such file or directory: './a/b/c'
      • 我更喜欢使用os.makedirs,如果父目录不存在,它会创建父目录。
      • 请注意,这会受到竞争条件的影响(如果其他人或其他线程在检查和调用 makedirs 之间创建目录)。如果文件夹存在,最好调用os.makedirs 并捕获异常。检查 SoF 以创建目录。
      【解决方案4】:

      这是EAFP 方式,可以避免竞争和不需要的系统调用:

      import errno
      import os
      import shutil
      
      src = "./blah.txt"
      dest = "./a/b/c/blah.txt"
      # with open(src, 'w'): pass # create the src file
      try:
          shutil.copy(src, dest)
      except IOError as e:
          # ENOENT(2): file does not exist, raised also on missing dest parent dir
          if e.errno != errno.ENOENT:
              raise
          # try creating parent directories
          os.makedirs(os.path.dirname(dest))
          shutil.copy(src, dest)
      

      【讨论】:

      • 如果在调用shutil.copy 之后但在调用os.makedirs 之前创建dest 目录,则仍然存在竞争。
      【解决方案5】:

      我如何使用 split 将目录移出路径

      dir_name, _ = os.path.split("./a/b/c/blah.txt")
      

      然后

      os.makedirs(dir_name,exist_ok=True)
      

      最后

      shutil.copy("./blah.txt", "./a/b/c/blah.txt")
      

      【讨论】:

      • 请注意,如果dir_name 包含不存在的子目录,您需要将代码调整为os.makedirs(dir_name + '/',exist_ok=True)
      【解决方案6】:

      对于 3.4/3.5+,您可以使用 pathlib:

      Path.mkdir(mode=0o777, parents=False, exist_ok=False)


      因此,如果可能要创建多个目录并且它们可能已经存在:

      pathlib.Path(dst).mkdir(parents=True, exist_ok=True)
      

      【讨论】:

        【解决方案7】:

        我的五分钱将是下一个方法:

        # Absolute destination path.
        dst_path = '/a/b/c/blah.txt'
        origin_path = './blah.txt'
        not os.path.exists(dst_path) or os.makedirs(dst_path)
        shutil.copy(origin_path, dst_path)
        

        【讨论】:

          【解决方案8】:

          许多其他答案适用于旧版本的 Python,尽管它们可能仍然有效,但您可以使用较新的 Python 更好地处理错误。

          如果您使用的是Python 3.3 或更新版本,我们可以捕获FileNotFoundError 而不是IOError。我们还想区分不存在的目标路径和不存在的源路径。我们想吞下前一个例外,而不是后者。

          最后,请注意os.makedirs() 会递归地一次创建一个丢失的目录——这意味着它不是原子操作。如果您有多个线程或进程可能尝试同时创建同一个目录树,您可能会看到意外行为。

          def copy_path(*, src, dst, dir_mode=0o777, follow_symlinks: bool = True):
              """
              Copy a source filesystem path to a destination path, creating parent
              directories if they don't exist.
          
              Args:
                  src: The source filesystem path to copy. This must exist on the
                      filesystem.
          
                  dst: The destination to copy to. If the parent directories for this
                      path do not exist, we will create them.
          
                  dir_mode: The Unix permissions to set for any newly created
                      directories.
          
                  follow_symlinks: Whether to follow symlinks during the copy.
          
              Returns:
                  Returns the destination path.
              """
              try:
                  return shutil.copy2(src=src, dst=dst, follow_symlinks=follow_symlinks)
              except FileNotFoundError as exc:
                  if exc.filename == dst and exc.filename2 is None:
                      parent = os.path.dirname(dst)
                      os.makedirs(name=parent, mode=dir_mode, exist_ok=True)
                      return shutil.copy2(
                          src=src,
                          dst=dst,
                          follow_symlinks=follow_symlinks,
                      )
                  raise
          
          

          【讨论】:

          • 在 try 块中您使用的是 shutil.copy(),但在 except 块中您使用的是 shutil.copy2()。我可以知道为什么吗?
          • @ShahadMahmud,这是一个错误。我已经更新了我的答案,两次都使用 copy2。
          猜你喜欢
          • 1970-01-01
          • 2014-03-08
          • 1970-01-01
          • 2013-12-23
          • 2021-10-08
          • 1970-01-01
          • 1970-01-01
          • 2014-10-09
          • 1970-01-01
          相关资源
          最近更新 更多