fenglinger 发表于 2019-10-9 18:53

python实现U盘备份脚本

本帖最后由 fenglinger 于 2019-11-6 20:07 编辑

## U盘备份脚本——python
### 想法的形成:

> 前几天我不小心把U盘连带钥匙一起丢了,U盘里面全是我从的各种资料,觉得好可惜,于是脑海里就有了这个想法。

---

### 一、 主要原理
- 主要原理就是通过python提供的os库进行操作系统的文件拷贝命令
- 通过多线程技术加快文件拷贝速度

### 二、特点
1. 开机自动运行,每次运行生成Log文件,以备出错时检查



2. U盘插入首先判断是否为需要复制的U盘,否则不进行复制
3. U盘内存放相应的配置文件,实现U盘内文件的版本更新(待实现)
4. 限制多线程的数量(19.11.06添加需求待实现)
5. 添加配置文件实现备份特定目录(19.11.06添加需求待实现)

楼主大学生准备出国所以忙于雅思,更新可能会很慢

### 三、 使用方法
>因为项目尚未完工,所以还没有进行打包,项目详情请访问(https://github.com/fenglinger/USB_bak)
1. 首先将文件拷贝至本地文件夹,确保电脑存在D盘盘符(因为U盘文件默认储存在`'D:\CopyFileRoot'`目录中)
2. 然后右键生成`USB_bak.pyw`文件的快捷方式,将快捷方式放入win系统的启动文件夹下。
3. 双击`USB_bak.pyw`文件即可运行,因为是后台进程所以不会有界面,打开进程管理器可以看到python虚拟机在运行。
4. 如要关闭程序只需打开进程管理器杀掉python虚拟机进程。

### 四、 具体代码
1. 首先声明以下全局变量以共使用
   
    ```python
    logRoot = "..\\Log"   # 本地log文件储存路径
    logName = "Log_"+ time.strftime("%Y%m%d",time.localtime(time.time()))   # Log文件名字,以时间命名
    targetRoot = 'D:\CopyFileRoot'# 目标目录
    oldDiskName = []# 旧的磁盘列表
    number = 0# 磁盘数,判断是否为第一次运行
    bakpath = ""    # u盘上的标记文件路径
    ```
2.程序初始化
   
    程序每次运行,都会判断程序是否是第一次在次电脑上运行,初次运行就创建相应的文件及文件目录
      ```python
      '''
      初始化函数
      '''
      def chackFirst():# 判断是否是第一次运行
            if not os.path.exists(logRoot):# 判断本地log文件夹是否存在
                os.mkdir(logRoot)
            if not os.path.exists(targetRoot):# 判断U盘本地备份路径是否存在
                os.mkdir(targetRoot)

      ```

3. 文件复制函数
   
   通过递归的方式进行文件的拷贝
       ```python

      '''
      从sourcepath复制文件和目录到targetPath
      '''
      def copyfile(sourcePath, targetPath, threadName):
            for f in os.listdir(sourcePath):
                if (f == 'System Volume Information'):# 过滤系统文件夹
                  continue

                f1 = os.path.join(sourcePath, f)# 连接源文件(目录)名
                f2 = os.path.join(targetPath, f)# 连接目标文件(目录)名


                if os.path.isfile(f1):# 如果为文件,则进行复制操作
                  if not os.path.exists(f2):
                        file1 = open(f1, 'rb')
                        file2 = open(f2, 'wb')
                        printLog(threadName + '-%s文件正在复制!' % (f1))
                        file2.write(file1.read())
                        printLog(threadName + '-%s文件复制成功!' % (f1))
                  else:
                        printLog(threadName + '文件已存在!' )
                else:# 如果为目录,创建新一级的目标目录,并递归操作
                  printLog(threadName + '-%s目录正在复制!' % (f2))
                  if not os.path.exists(f2):
                        os.mkdir(f2)
                        printLog(threadName + '-%s目标目录创建成功!' % (f2))
                  else:
                        printLog('复制失败,原因:目录已存在!')
                  copyfile(f1, f2, threadName)
                  printLog(threadName + '-%s目录复制成功!' % (f1))


         '''
            复制盘符name中的文件到目标目录中
      '''
      def copy(name, threadName):
            f = open(bakpath, 'a+')   #以追加模式写入。此处是为了之后实现对于U盘文件的版本更新
            timeNow = str(time.strftime('%Y%m%d%H%M%S', time.localtime(time.time())))# 获取当前时间字串
            targetPath = os.path.join(targetRoot, name[:1])# 创建一个新目录,使用目标目录+盘符作为名字
            if not os.path.exists(targetPath):
                os.mkdir(targetPath)# 创建新的目录
            copyfile(name, targetPath, threadName)# 复制文件
            f.close()
            printLog(threadName + '-新磁盘:%s盘 复制完毕!' % (name[:1]))

      
      ```

4.处理盘符信息:

    获取当前系统内的盘符列表并判断是否有U盘插入
      ```python
      '''
      获取磁盘信息,并与上次获取的信息进行比较,判断是否有新的磁盘添加进来
      '''

      def getDiskMessage():
            global oldDiskName# 声明全局变量
            global number

            if number == 0:# 第一次操作,先获取一遍磁盘数据,然后返回
                for disk in psutil.disk_partitions():
                  number = number + 1
                  oldDiskName.append(disk.device[:2])# 获取盘符信息
                return

            newDiskName = []# 保存新获取的磁盘信息
            for disk in psutil.disk_partitions():
                newDiskName.append(disk.device[:2])# 获取新的磁盘信息

            newDiskList = arrayCompare(oldDiskName, newDiskName)# 获取新增盘符列表

            oldDiskName.clear()# 清除旧盘符列表
            oldDiskName = newDiskName[:]# 复制新盘符列表给旧盘符列表
            return newDiskList


      '''
      比较两个磁盘盘符列表,并返回新盘符列表中旧盘符列表没有的盘符名列表
      '''


      def arrayCompare(oldDiskName, newDiskName):
            newDiskList = []
            for name in newDiskName:
                if name not in oldDiskName:# 旧盘符中没有,则添加这个到新增盘符列表中
                  newDiskList.append(name)
            return newDiskList
      ```

5.整体代码:

      ```python
      import os
      import time
      import psutil
      from threading import Thread

      logRoot = "..\\Log"
      logName = "Log_"+ time.strftime("%Y%m%d",time.localtime(time.time()))
      targetRoot = 'D:\CopyFileRoot'# 目标目录
      oldDiskName = []# 旧的磁盘列表
      number = 0# 磁盘数,判断是否为第一次运行
      bakpath = ""    # u盘上的标记文件路径

      '''
            初始化函数
      '''
      def chackFirst():# 判断是否是第一次运行
            if not os.path.exists(logRoot):# 判断本地log文件夹是否存在
                os.mkdir(logRoot)
            if not os.path.exists(targetRoot):# 判断本地备份文件是否存在
                os.mkdir(targetRoot)

      '''
            写Log函数
      '''
      def printLog(content):
            f = open(os.path.join(logRoot,logName),'a+')
            f.write(time.strftime("%H:%M:%S    ",time.localtime(time.time())))
            f.write(content+'\n')
            f.close()



      '''
      从sourcepath复制文件和目录到targetPath
      '''


      def copyfile(sourcePath, targetPath, threadName):
            for f in os.listdir(sourcePath):
                if (f == 'System Volume Information'):# 过滤系统文件夹
                  continue

                f1 = os.path.join(sourcePath, f)# 连接源文件(目录)名
                f2 = os.path.join(targetPath, f)# 连接目标文件(目录)名


                if os.path.isfile(f1):# 如果为文件,则进行复制操作
                  if not os.path.exists(f2):
                        file1 = open(f1, 'rb')
                        file2 = open(f2, 'wb')
                        printLog(threadName + '-%s文件正在复制!' % (f1))
                        file2.write(file1.read())
                        printLog(threadName + '-%s文件复制成功!' % (f1))
                  else:
                        printLog(threadName + '文件已存在!' )
                else:# 如果为目录,创建新一级的目标目录,并递归操作
                  printLog(threadName + '-%s目录正在复制!' % (f2))
                  if not os.path.exists(f2):
                        os.mkdir(f2)
                        printLog(threadName + '-%s目标目录创建成功!' % (f2))
                  else:
                        printLog('复制失败,原因:目录已存在!')
                  copyfile(f1, f2, threadName)
                  printLog(threadName + '-%s目录复制成功!' % (f1))

      '''
      获取磁盘信息,并与上次获取的信息进行比较,判断是否有新的磁盘添加进来
      '''

      def getDiskMessage():
            global oldDiskName# 声明全局变量
            global number

            if number == 0:# 第一次操作,先获取一遍磁盘数据,然后返回
                for disk in psutil.disk_partitions():
                  number = number + 1
                  oldDiskName.append(disk.device[:2])# 获取盘符信息
                return

            newDiskName = []# 保存新获取的磁盘信息
            for disk in psutil.disk_partitions():
                newDiskName.append(disk.device[:2])# 获取新的磁盘信息

            newDiskList = arrayCompare(oldDiskName, newDiskName)# 获取新增盘符列表

            oldDiskName.clear()# 清除旧盘符列表
            oldDiskName = newDiskName[:]# 复制新盘符列表给旧盘符列表
            return newDiskList


      '''
      比较两个磁盘盘符列表,并返回新盘符列表中旧盘符列表没有的盘符名列表
      '''


      def arrayCompare(oldDiskName, newDiskName):
            newDiskList = []
            for name in newDiskName:
                if name not in oldDiskName:# 旧盘符中没有,则添加这个到新增盘符列表中
                  newDiskList.append(name)
            return newDiskList


      '''
      复制盘符name中的文件到目标目录中
      '''


      def copy(name, threadName):
                f = open(bakpath, 'a+')   #以追加模式写入
                timeNow = str(time.strftime('%Y%m%d%H%M%S', time.localtime(time.time())))# 获取当前时间字串
                targetPath = os.path.join(targetRoot, name[:1])# 创建一个新目录,使用目标目录+盘符作为名字
                if not os.path.exists(targetPath):
                  os.mkdir(targetPath)# 创建新的目录
                copyfile(name, targetPath, threadName)# 复制文件
                f.close()
                printLog(threadName + '-新磁盘:%s盘 复制完毕!' % (name[:1]))



      if __name__ == '__main__':
            getDiskMessage()# 获取初始数据
            # targetRoot = chackFirst()# 软件初始化
            threadCount = 0# 线程计数
            printLog("开始运行")
            while True:

                newDiskList = getDiskMessage()# 获取新数据
                if len(newDiskList) > 0:    # 检测到有新u盘插入
                  printLog('当前磁盘列表:' + str(oldDiskName))
                  printLog('新磁盘列表:' + str(newDiskList))
                  time.sleep(10)      # 延迟等待u盘加载
                  for name in newDiskList:# 根据新获取到的数据去复制文件
                        bakpath = os.path.join(name, 'USB.bak')
                        t = os.path.exists(bakpath)
                        if t:# 没有文件则不是需要备份的u盘
                            # copy(name, 'thread_' + str(threadCount))
                            thread = Thread(target=copy, args=(name, 'thread_' + str(threadCount),))# 创建线程去复制指定磁盘
                            thread.start()# 开启线程
                            printLog('thread_' + str(threadCount) + '-开始复制%s盘文件...' % (name[:1]))
                            threadCount = threadCount + 1# 线程计数+1
                time.sleep(10)   # 延时10秒进行下一次数据获取
      ```

随梦期初 发表于 2019-10-17 09:48

这个只能复制一次吧?如果U盘中文件有过更改,没办法直接同步到备份中吧

fenglinger 发表于 2019-10-17 10:49

随梦期初 发表于 2019-10-17 09:48
这个只能复制一次吧?如果U盘中文件有过更改,没办法直接同步到备份中吧

对,所以还在改进,用pandas写数据进行版本更新

喷火龙的喷 发表于 2019-11-4 09:46

牛批,支持一下,期待更完善

xie8xie 发表于 2019-11-4 19:16

谢谢分享,期待更完善。

zzgaot 发表于 2019-11-5 21:00

很详细的,慢慢学习,等待你的更新。
楼主是不是可以同时做个手机备份的项目呢?比如说把手机里固定的几个文件夹里的一些图片,文档之类的复制到自己的电脑里,做好备份,每次还得自己定期一个个对,如果可以插上手机,直接就把新的内容备份上就省事了。应该和这个原理差不多的吧。

zhoujinjian 发表于 2019-11-6 15:59

不过文件太大会不会卡死啊

fenglinger 发表于 2019-11-6 20:00

zzgaot 发表于 2019-11-5 21:00
很详细的,慢慢学习,等待你的更新。
楼主是不是可以同时做个手机备份的项目呢?比如说把手机里固定的几个 ...

原理上应该差不多,就是特定访问某一个目录就好了,不过前提是安卓连接电脑自动打开数据传输

fenglinger 发表于 2019-11-6 20:03

zhoujinjian 发表于 2019-11-6 15:59
不过文件太大会不会卡死啊

如果用多线程的话,少量的文件应该不会,倒是提醒我了,应该限制一下同一时间启动的线程数量。不过多线程的话U盘的负荷会比较大,我每次用完发热量挺大的,确实应该限制一下线程数量
页: [1]
查看完整版本: python实现U盘备份脚本