将图像复制到特定文件夹

copying images to certain folder

我是新手 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 如果重要的话

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

此方法遍历了许多非文件的内容(例如 /dev/proc 文件夹)以及您的文件不太可能存在的系统文件夹。 (您真的不希望任何图片都在 /bin/usr/bin 之下,对吗?)

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

另一个优化可能根本不使用 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.

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

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}

如果您需要 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()

由于您在 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()