首页 > 其他分享 >golang 多线程 备份文件夹到兄弟层级 wail group

golang 多线程 备份文件夹到兄弟层级 wail group

时间:2025-01-20 22:23:22浏览次数:1  
标签:group string err 备份文件 func inputPath return 多线程 my

golang 多线程 备份文件夹到兄弟层级 wail group

D:\GolangTools\src\config\config.go


package config

type ConfigHandler struct {
	includeDirNames  []string
	includeFileNames []string
	excludeDirNames  []string
	excludeFileNames []string
}

func NewConfigHandler(includeDirNames []string, includeFileNames []string, excludeDirNames []string, excludeFileNames []string) *ConfigHandler {
	return &ConfigHandler{
		includeDirNames:  includeDirNames,
		includeFileNames: includeFileNames,
		excludeDirNames:  excludeDirNames,
		excludeFileNames: excludeFileNames,
	}
}

func (my *ConfigHandler) SetIncludeDirNames(includeDirNames []string) {
	my.includeDirNames = includeDirNames
}
func (my *ConfigHandler) SetIncludeFileNames(includeFileNames []string) {
	my.includeFileNames = includeFileNames
}
func (my *ConfigHandler) SetExcludeDirNames(excludeDirNames []string) {
	my.excludeDirNames = excludeDirNames
}
func (my *ConfigHandler) SetExcludeFileNames(excludeFileNames []string) {
	my.excludeFileNames = excludeFileNames
}

func (my *ConfigHandler) GetIncludeDirNames() []string {
	return my.includeDirNames
}
func (my *ConfigHandler) GetIncludeFileNames() []string {
	return my.includeFileNames
}
func (my *ConfigHandler) GetExcludeDirNames() []string {
	return my.excludeDirNames
}
func (my *ConfigHandler) GetExcludeFileNames() []string {
	return my.excludeFileNames
}

D:\GolangTools\src\input\inputHandler.go


package input

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"
)

type IInputHandler interface {
	HandleInputArgs()
}

type InputHandler struct {
	function      string
	inputPath     string
	dstFolderPath string
}

func (my *InputHandler) HandleInputArgs() error {
	// os.Args 是一个字符串切片,存储了命令行参数
	// 其中 os.Args[0] 是程序本身的名称,后续元素是传递的参数
	args := os.Args
	if len(args) < 3 {
		return fmt.Errorf("参数不足,请输入两个参数")
	}
	for i, arg := range args {
		if i == 0 {
			continue
		}
		// fmt.Printf("参数 %d 是: %s\n", i, arg)
		parts := strings.Split(arg, "=")
		if len(parts) != 2 {
			return fmt.Errorf("参数格式错误,请使用格式: key=value")
		}
		switch parts[0] {
		case "--function":
			my.function = parts[1]
			continue
		case "--folderpath":
			my.inputPath = parts[1]
			continue
		default:
			return fmt.Errorf("未知参数: %s", parts[0])
		}
	}
	return nil
}

func NewInputHandler() *InputHandler {
	return &InputHandler{}
}

func (my *InputHandler) GetFunction() string {
	return my.function
}

func (my *InputHandler) GetInputPath() string {
	return my.inputPath
}

func (my *InputHandler) SetFunction(function string) {
	my.function = function
}

func (my *InputHandler) SetInputPath(inputPath string) {
	my.inputPath = inputPath
}

func (my *InputHandler) SetDstFolderPath() {
	fmt.Print("请输入注解后缀:")
	suffix, _ := bufio.NewReader(os.Stdin).ReadString('\n') // 读取直到换行符
	suffix = strings.TrimSpace(suffix)                      // 去除字符串两端的空白字符,包括换行符
	originlFolderName := filepath.Base(my.inputPath)
	fatherFolderPath := filepath.Dir(my.inputPath)
	my.dstFolderPath = filepath.Join(fatherFolderPath, originlFolderName+"_"+time.Now().Format("20060102_150405")) + "_" + suffix
}

func (my *InputHandler) GetDstFolderPath() string {
	return my.dstFolderPath
}

D:\GolangTools\src\path\pathHandler.go


package path

import (
	"GolangTools/src/config"
	"GolangTools/src/input"
	"GolangTools/src/utils"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"
)

type IPathHandler interface {
	GetAllCodeFiles() []string
}
type PathHandler struct {
	allFilePaths  []string
	inputHandler  *input.InputHandler
	configHandler *config.ConfigHandler
}

func NewPathHandler(inputHandler *input.InputHandler, configHandler *config.ConfigHandler) *PathHandler {
	return &PathHandler{
		inputHandler:  inputHandler,
		configHandler: configHandler,
	}
}
func (my *PathHandler) GetAllCodeFiles() ([]string, error) {

	inputPath := my.inputHandler.GetInputPath()
	// 使用 os.ReadDir 读取目录
	topDirs, err := my.GetTopDirs(inputPath)
	if err != nil {
		return nil, err
	}

	var producerWg sync.WaitGroup
	var consumerWg sync.WaitGroup
	var copyWg sync.WaitGroup

	// 创建一个容量为 3 的有缓冲的 int 类型 channel
	bufferedChannel := make(chan string, 100)

	for _, topDir := range topDirs {
		folderName := filepath.Base(topDir)
		if utils.ExsitInSlice(my.configHandler.GetExcludeDirNames(), folderName) {
			if !utils.ExsitInSlice(my.configHandler.GetIncludeDirNames(), folderName) {
				continue
			} else {
				println("skip:", filepath.Join(inputPath, folderName))
			}
		}
		producerWg.Add(1)
		go func(topDir string) {
			defer producerWg.Done()
			my.TraverseDir(topDir, bufferedChannel, &producerWg)
		}(topDir)
	}

	consumerWg.Add(1)
	go func() {
		defer consumerWg.Done()
		my.CollectDeepDirs(bufferedChannel, &consumerWg)
	}()
	// 关闭通道的 goroutine
	go func() {
		producerWg.Wait()
		close(bufferedChannel)
	}()
	consumerWg.Wait()

	// 关闭 channel
	println("收集完毕Done,开始拷贝!!!")
	// println(my.inputHandler.GetDstFolderPath())
	topFiles, err := my.GetTopFiles(my.inputHandler.GetInputPath())
	if err != nil {
		println(err.Error())
		return nil, err
	}

	my.allFilePaths = append(my.allFilePaths, topFiles...)
	for _, filePath := range my.allFilePaths {
		copyWg.Add(1)
		go func(copyWg *sync.WaitGroup) {
			defer copyWg.Done()
			parts := strings.Split(filePath, my.inputHandler.GetInputPath())
			dstFilePath := filepath.Join(my.inputHandler.GetDstFolderPath(), parts[1])
			_, err := utils.CopyFile(filePath, dstFilePath, true)
			if err != nil {
				println("copy error:" + filePath + err.Error())
			}
			println("copy:" + filePath)
		}(&copyWg)
	}
	copyWg.Wait()
	return nil, nil
}

func (my *PathHandler) GetTopFiles(inputPath string) ([]string, error) {
	entries, err := os.ReadDir(inputPath)
	if err != nil {
		return nil, fmt.Errorf("读取目录:%s出错:%v", inputPath, err)
	}
	folderPaths := make([]string, 0)
	// 遍历目录中的条目
	for _, entry := range entries {
		if !entry.IsDir() {
			// fmt.Println("子文件夹:", entry.Name())
			// fmt.Println("子文件夹:", filepath.Join(inputPath, entry.Name()))
			folderPaths = append(folderPaths, filepath.Join(inputPath, entry.Name()))
		}
	}
	return folderPaths, nil
}
func (my *PathHandler) GetTopDirs(inputPath string) ([]string, error) {
	entries, err := os.ReadDir(inputPath)
	if err != nil {
		return nil, fmt.Errorf("读取目录:%s出错:%v", inputPath, err)
	}
	folderPaths := make([]string, 0)
	// 遍历目录中的条目
	for _, entry := range entries {
		if entry.IsDir() {
			// fmt.Println("子文件夹:", entry.Name())
			// fmt.Println("子文件夹:", filepath.Join(inputPath, entry.Name()))
			folderPaths = append(folderPaths, filepath.Join(inputPath, entry.Name()))
		}
	}
	return folderPaths, nil
}

func (my *PathHandler) TraverseDir(inputPath string, c chan<- string, wg *sync.WaitGroup) error {
	entries, err := os.ReadDir(inputPath)
	if err != nil {
		return fmt.Errorf("读取目录:%s出错:%v", inputPath, err)
	}
	// folderPaths := make([]string, 0)
	// 遍历目录中的条目
	for _, entry := range entries {
		if entry.IsDir() {
			folderName := entry.Name()
			if !utils.ExsitInSlice(my.configHandler.GetExcludeDirNames(), folderName) {
				my.TraverseDir(filepath.Join(inputPath, folderName), c, wg)
				continue
			}
			if !utils.ExsitInSlice(my.configHandler.GetIncludeDirNames(), folderName) {
				println("skip:", filepath.Join(inputPath, folderName))
				continue
			}
			my.TraverseDir(filepath.Join(inputPath, folderName), c, wg)
		} else {
			fileName := entry.Name()
			if !utils.ExsitInSlice(my.configHandler.GetExcludeFileNames(), fileName) {
				// println("copy:", filepath.Join(inputPath, fileName))
				c <- filepath.Join(inputPath, fileName)
				continue
			}
			if !utils.ExsitInSlice(my.configHandler.GetIncludeFileNames(), fileName) {
				println("skip:", filepath.Join(inputPath, fileName))
				continue
			}
			c <- filepath.Join(inputPath, entry.Name())
		}
	}
	return nil
}

func (my *PathHandler) CollectDeepDirs(c <-chan string, wg *sync.WaitGroup) error {
	for data := range c {
		my.allFilePaths = append(my.allFilePaths, data)
	}
	return nil
}

D:\GolangTools\src\utils\utils.go


package utils

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
)

func ExsitInSlice(slice []string, str string) bool {
	for _, s := range slice {
		if s == str {
			return true
		}
	}
	return false
}

func CopyFile(src, dst string, overwrite bool) (int64, error) {
	sourceFileStat, err := os.Stat(src)
	if err != nil {
		return 0, err
	}
	// Check if source and destination are the same file
	if !overwrite {
		destFileStat, err := os.Stat(dst)
		if err == nil { // 如果目标文件存在
			if os.SameFile(sourceFileStat, destFileStat) {
				return 0, fmt.Errorf("source and destination are the same file")
			}
		} else if !os.IsNotExist(err) { // 如果不是因为目标文件不存在而产生的错误
			return 0, err
		}
	}

	// 打开源文件
	source, err := os.Open(src)
	if err != nil {
		return 0, err
	}
	defer source.Close()

	// 创建或打开目标文件
	// 确保目标路径的所有父目录都存在
	if err := os.MkdirAll(filepath.Dir(dst), os.ModePerm); err != nil {
		return 0, fmt.Errorf("creating parent directories for destination %s: %v", dst, err)
	}
	var flags int = os.O_CREATE | os.O_WRONLY
	if overwrite {
		flags |= os.O_TRUNC // 如果允许覆盖,则截断文件
	} else {
		flags |= os.O_EXCL // 如果不允许覆盖,则使用 O_EXCL 防止覆盖
	}
	destination, err := os.OpenFile(dst, flags, sourceFileStat.Mode())
	if err != nil {
		return 0, err
	}
	defer destination.Close()

	// 复制文件内容
	nBytes, err := io.Copy(destination, source)
	return nBytes, err
}

D:\GolangTools\go.mod


module GolangTools

go 1.23.0

D:\GolangTools\main.go


package main

import (
	"GolangTools/src/config"
	"GolangTools/src/input"
	"GolangTools/src/path"
	"fmt"
	"time"
)

func main() {

	start := time.Now()
	var includeDirNames []string = []string{"", ""}
	var includeFileNames []string = []string{"", ""}
	var excludeDirNames []string = []string{".git", "obj", "bin", ".vscode", "zz_note", "zz_zz", "zz_pro", "zz_res", "zz_config"}
	var excludeFileNames []string = []string{"", ""}
	configHandler := config.NewConfigHandler(includeDirNames, includeFileNames, excludeDirNames, excludeFileNames)
	inputHandler := input.NewInputHandler()
	err := inputHandler.HandleInputArgs()
	if err != nil {
		panic(err)
	}
	inputHandler.SetDstFolderPath()
	pathHandler := path.NewPathHandler(inputHandler, configHandler)
	pathHandler.GetAllCodeFiles()
	duration := time.Since(start)
	fmt.Printf("程序执行时间: %v\n", duration)
}

D:\GolangTools\run.bat


@REM set scriptPath=D:\DotnetTools
set scriptPath=%cd%
go run main.go --function=code2md --folderpath="%scriptPath%"



标签:group,string,err,备份文件,func,inputPath,return,多线程,my
From: https://www.cnblogs.com/zhuoss/p/18682584

相关文章

  • hgroup
    以下由AI生成:<hgroup>是一个HTML元素,用于对标题(<h1>到<h6>)进行分组,通常用于将主标题和副标题组合在一起。它在HTML5中被引入,但在HTML5.2中被废弃,并且在后续的HTML规范中不再被推荐使用。<hgroup> 的用途(已废弃)在<hgroup>被推荐使用时,它的主要目的是将多个标题元......
  • Windows图形界面(GUI)-QT-C/C++ - Qt QGroupBox详解教程
    公开视频-> 链接点击跳转公开课程博客首页-> ​​​链接点击跳转博客主页目录QGroupBox基础概念QGroupBox简介使用场景QGroupBox常见样式框架和标题可启用/禁用扁平化样式QGroupBox属性设置标题​编辑对齐方式启用状态​编辑扁平化样式QGroupBox的内容操作......
  • RK3588平台开发系列讲解(调试篇)CGroup 精细化的控制
    文章目录一、CPU与CGroup二、限制进程的CPU资源占用三、cpu.shares:多个cgroup组的权重划分四、sched_autogroup沉淀、分享、成长,让自己和他人都能有所收获!......
  • 基于C#实现多线程启动停止暂停继续
    大家好!我是付工。大部分初学者在学习C#上位机编程时,多线程是一个很难逾越的鸿沟,不合理地使用多线程,会导致经常出现各种奇怪的问题,这也是很多初学者不敢使用多线程的原因。但是在实际开发中,多线程是一个不可避免的技术栈,基本上每个项目都会使用到,因此学好多线程技术,很重要。一、......
  • JAVA多线程
           一多线程基础知识相关概念进程(Process):进程是程序的基本执行实体。进程是操作系统分配资源的基本单位。每个进程都有自己的内存空间、代码段、数据段等。进程之间相互独立,一个进程的崩溃不会影响其他进程。进程是程序的基本执行实体。线程(Thread): ......
  • Python 进阶 - 多线程(一)
    Python进阶-多线程相关概念解释器GILthreading方法属性threading.enumerate()threading.active_count()threading.current_thread()threading.get_ident()threading.main_thread()threading.stack_size([size])threading.get_native_id()threading.TIMEOUT_MAX线程......
  • 请为什么说js是单线程,而不是多线程呢?
    JavaScript被设计为单线程语言,而不是多线程,主要基于以下几个原因:简化并发问题:单线程模型可以显著简化并发带来的复杂性。在多线程环境中,开发人员需要处理诸如竞态条件、死锁等复杂的同步问题。而JavaScript的单线程设计避免了这些问题,使得代码更加易于理解和维护。避免浏览......
  • libcurl多线程下载,支持断点续传
    libcurl多线程下载一步步实现创建时间:2024年12月1日17:35标签:libcurl,linux,下载,多线程最后编辑:2025年1月16日23:43平台是WSL的Ubuntu22,使用Gcc编译。单线程下载编译命令gcc-otranstrans.c-lcurl/*trans.c*/#include<curl/curl.h>#include<stdio.h>......
  • 在线客服系统 QPS 突破 240/秒,连接数突破 4000,日请求数接近1000万次,.NET 多线程技术的
    背景我在业余时间开发了一款自己的独立产品:升讯威在线客服与营销系统。陆陆续续开发了几年,从一开始的偶有用户尝试,到如今的QPS突破240次/秒,连接数突破4000,日请求数接近1000万。(PS:阿里云真贵啊)在这篇文章中,我将简要介绍我在技术上做了哪些工作,我是如何做到的。PS:虽......
  • 11 多线程详解
    线程简介程序:程序就像一堆写好的指令和数据放在一起,它是静止的,不会自己动起来。进程(Process):进程是把程序真正运行起来的过程,它是动态的,系统会给它分配各种资源,比如内存等。线程(Thread):一个进程里通常会有好几个线程,最少也得有一个,不然进程就没啥用了。线程是CPU安排干活和实际......