【问题标题】:copying images to certain folder将图像复制到特定文件夹
【发布时间】:2020-08-03 12:59:42
【问题描述】:

我是python新手,我正在尝试构建一个可以从整个服务器收集图像的小脚本,我有一定的图像命名:

AMZ_1004.jpg
AMZ_1272.jpg
GOO_1.jpeg
GOO_2.png

我希望脚本浏览每个目录并将文件复制(而不是移动)到 AMZ 和 GOO

import shutil,os

goo_dst = '/home/usr2/Pictures/GOO'
amz_dst = '/home/usr2/Pictures/AMZ'
os.makedirs(goo_dst,exist_ok=1)
os.makedirs(amz_dst,exist_ok=1)
for root, dirs, files in os.walk('/'):
    for name in files:
        path = os.path.join(root, name)
        if name.startswith('GOO_') and (name.endswith('.jpg') or name.endswith('.jpeg') or name.endswith('.png')):
            shutil.copyfile(path, goo_dst)
        elif name.startswith('AMZ_') and name.endswith('.jpg'):
            shutil.copyfile(path, amz_dst)

脚本运行正常,有没有办法加快进程?

脚本可以在 Arch Linux 上运行

【问题讨论】:

  • @CavinDsouza 我运行 python 2.7 ,不认为会工作
  • 哦,开枪。似乎os.walk() 递归调用os.listdir() 也获取文件的元信息,这使得它变慢。 os.scandir() 类似且更快,但我认为 v2.7 不支持它
  • shutil 是复制文件的最快方法吗?仅从名称来看,它似乎可能会用于一个单独的进程,这会产生很多开销。
  • 你可以试试多处理

标签: python python-2.7 python-2.x


【解决方案1】:

您可以对脚本进行的最大优化不是在文件系统根目录上开始搜索。

此方法会检查许多不是文件的内容(例如/dev/proc 文件夹)以及不太可能存在文件的系统文件夹。 (你真的不希望任何图像在/bin/usr/bin 下,对吧?)

尝试缩小真正的搜索路径,例如 Apache 文件夹所在的/var/www

另一个优化可能根本不使用 Python,而是直接使用 shell 脚本:

#!/bin/sh
GOO_DST='/home/usr2/Pictures/GOO'
AMZ_DST='/home/usr2/Pictures/AMZ'

mkdir -p ${GOO_DST}
mkdir -p ${AMZ_DST}

find / -type f -name 'GOO_*.jpg' -o -name 'GOO_*.jpeg' -o -name 'GOO_*.png' -exec cp {} ${GOO_DST} \;

find / -type f -name 'AMZ_*.jpg' -exec cp {} ${AMZ_DST} \;

find 实用程序应该比手动遍历提供更快的结果。

如果你坚持使用 Python,至少移动 path = os.path.join(root, name) 以避免对不相关的文件(大多数文件)进行一些额外的工作。 这是一个很小的优化,但仍然可以提供帮助。

另一种选择是使用多线程来并行化搜索,但您需要手动决定每个线程将搜索文件系统的哪个部分。

如果 2 个线程遍历同一个文件夹,那将是更大的时间浪费。 另外,请注意,多线程处理此脚本可能会导致它在运行时占用更多 CPU。

Read here for more details.

【讨论】:

    【解决方案2】:

    如果你需要python实现,你可以通过shutil的异步调用来加速(这应该允许shutil为不同的文件并行运行)

    import asyncio
    
    @asyncio.coroutine
    def async_copyfile(src, dst):
        yield shutil.copyfile(src, dst)    
    
    def generate_tasks():
        goo_dst = '/home/usr2/Pictures/GOO'
        amz_dst = '/home/usr2/Pictures/AMZ'
        os.makedirs(goo_dst,exist_ok=1)
        os.makedirs(amz_dst,exist_ok=1)
        for root, dirs, files in os.walk('/'):
            for name in files:
                path = os.path.join(root, name)
                if name.startswith('GOO_') and (name.endswith('.jpg') or name.endswith('.jpeg') or name.endswith('.png')):
                    yield (path, goo_dst)
                elif name.startswith('AMZ_') and name.endswith('.jpg'):
                    yield (path, amz_dst)
    
    loop = asyncio.get_event_loop()
    tasks = [asyncio.ensure_future(async_copyfile(src, dst)) for src, dst in generate_tasks()]
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()
    

    【讨论】:

    • 我喜欢这个解决方案,但是 OP 正在使用的 python-2.7 中是否存在 asyncio?我以为 asyncio 仅在 3.4 中添加。
    【解决方案3】:

    我认为您可以使用rsync 而不是使用python 脚本。代表“远程同步”的rsync是一个远程和本地文件同步工具。它使用一种算法,通过仅移动已更改的文件部分来最小化复制的数据量。例如你的情况;

    rsync -a "$PWD" --include='*/' --include='GOO_*.jpg' --include='GOO_*.jpeg' --include='GOO_*.png' --exclude='*' /home/usr2/Pictures/GOO/
    
    rsync -a "$PWD" --include='*/' --include='AMZ_*.jpg' --exclude='*' /home/usr2/Pictures/AMZ/
    

    或者你也可以使用简单的方式;

    rsync $(pwd)/GOO_*.{jpg,jpeg,png} /home/usr2/Pictures/GOO/
    
    rsync $(pwd)/AMZ_*.jpg /home/usr2/Pictures/AMZ/
    

    最后和mkdir结合起来(如果你还需要创建路径);

    #!/bin/bash
    
    GOO_PATH='/home/usr2/Pictures/GOO/'
    AMZ_PATH='/home/usr2/Pictures/AMZ/'
    
    mkdir -p ${GOO_PATH} && rsync $(pwd)/GOO_*.{jpg,jpeg,png} ${GOO_PATH}
    mkdir -p ${AMZ_PATH} && rsync $(pwd)/AMZ_*.jpg ${AMZ_PATH}
    

    【讨论】:

      【解决方案4】:

      由于您在 linux 上使用 python2.7,您可以使用以下优化:

      • 使用内置系统cp
      • 为副本批处理文件
      • TODO:批量调用find_files迭代器

      我会让你给他们计时,看看他们是否有帮助。

      由于每个副本都在争用磁盘,因此我认为并行化副本不会有太大帮助。

      import shutil, os
      
      goo_dst = '/home/usr2/Pictures/GOO'
      amz_dst = '/home/usr2/Pictures/AMZ'
      
      
      def find_files(path='/'):
          for root, dirs, files in os.walk(path):
              for name in files:
                  path = os.path.join(root, name)
                  if name.startswith('GOO_') and (name.endswith('.jpg') or name.endswith('.jpeg') or name.endswith('.png')):
                      yield(path, goo_dst)
                  elif name.startswith('AMZ_') and name.endswith('.jpg'):
                      yield(path, amz_dst)
      
      def my_cp(dst, files):
          # we use array form to avoid problems with file paths containing spaces
          return subprocess.call(["cp"] + files, shell=False)
      
      def main:
          os.makedirs(goo_dst, exist_ok=1)
          os.makedirs(amz_dst, exist_ok=1)
      
          files = {goo_dst: [],
                   amz_dst: []}
          base_path = '/' # limit this if possible, www/data perhaps?
          min_copy = 50 # tune for your needs
          for path, dest in find_files(base_path):
              files[dest].append(path)
              if len(files[dest]) > min_copy:
                  my_cp(dest, files[dest])
                  files[dest] = []
          # clean up any remaining files
          for dest, paths in files.iter_items(): 
              if paths:
                  my_cp(paths, dest)
      main()
      

      【讨论】:

        猜你喜欢
        • 2018-05-17
        • 1970-01-01
        • 1970-01-01
        • 2018-12-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-12-02
        相关资源
        最近更新 更多